一.建立TCP的服务端
1.加载库
2.创建监听套接字
3.绑定IP地址
4.监听
5.接受链接
6.接收数据
7.发送数据
8.关闭套接字,卸载库
#include<iostream>
#include<stdio.h>
#include<winsock2.h>
#include <Ws2tcpip.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
int main()
{
//1.加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
{
printf("The Winsock 2.2 dll was found okay\n");
}
//2.创建套接字(该套接字为监听套接字,主要为了监听客户端的链接请求的)
SOCKET socklis = INVALID_SOCKET;
socklis = socket(2, SOCK_STREAM, IPPROTO_TCP);//第一个参数af,有ipv4 ipv6//第二个参数type:SOCK_STREAM(tcp),SOCK_DGRAM(udp)
//第三个参数使用的协议:protocol IPPROTO_TCP IPPROTO_UDP
if (INVALID_SOCKET == socklis)
{
cout << "socket error" << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
else
{
cout << "socket success" << endl;
}
//3.绑定IP地址 //htons将整形变量从主机字节序转换成网络字节序inet_addr()将Ip地址从字符串转换为u_long
sockaddr_in soserver;
soserver.sin_family = AF_INET;
soserver.sin_addr.S_un.S_addr = INADDR_ANY;
soserver.sin_port = htons(54321);
err = bind(socklis, (SOCKADDR*)&soserver, sizeof(soserver)); //作为服务器要进行监听要绑定所有的IP地址inaddr_any
if (err == 0)
{
cout << "bind success" << endl;
}
else if (SOCKET_ERROR == err)
{
cout << "bind err" << WSAGetLastError() << endl;
closesocket(socklis);
WSACleanup();
}
//4.监听。服务端要监听来自客户端的链接请求
int errlis=listen(socklis, SOMAXCONN);
if (errlis == 0)
{
cout << "listen success" << endl;
}
else
{
cout << "listen error" << WSAGetLastError() << endl;
closesocket(socklis);
WSACleanup();
return 0;
}
sockaddr_in soclient;
int size = sizeof(soclient);
while (true)
{
//5.接收链接。如果服务端接受了一个客户端的链接,那么将为其单独创建一个套接字与其通信
SOCKET sock = accept(socklis, (SOCKADDR*)&soclient, &size);
if (sock != INVALID_SOCKET)
{
cout << "accept success" << endl;
}
else
{
cout << "accept err" << WSAGetLastError() << endl;
closesocket(socklis);
WSACleanup();
return 1;
}
int nrecvbuf = 0;
char recvbuf[1025] = "";
int nsenbuf = 0;
char senbuf[1024] = "";
while (true)
{
//6.接收数据
int a = recv(sock, recvbuf, sizeof(recvbuf), 0);
if (a > 0)
{
printf("Bytes received: %d\n", a);
cout << "客户端发送了:" << recvbuf << endl;
}
else if (a == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
gets_s(senbuf);
//7.发送数据
int b = send(sock, senbuf, sizeof(senbuf), 0);
if (b == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
}
closesocket(sock);
}
//8.关闭套接字和卸载库
closesocket(socklis);
WSACleanup();
return 0;
}
关于缓冲区的问题:(下面说的是内核的缓冲区,而代码中的serbuf和clibuf是自己创建的缓冲区,为了方便调用以及显示内核缓冲区的内容)
每个
socket
被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。
TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,比如nagle算法,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。
read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
二.TCP的客户端建立
1.加载库
2.创建套接字
3.链接
4.发送数据
5.接收数据
6.关闭套接字和卸载库
#include<iostream>
#include<stdio.h>
#include<winsock2.h>
#include <Ws2tcpip.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
int main()
{
//1.加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
{
printf("The Winsock 2.2 dll was found okay\n");
}
//2.创建套接字
SOCKET sock = INVALID_SOCKET;
sock = socket(2, SOCK_STREAM, IPPROTO_TCP);//第一个参数af,有ipv4 ipv6//第二个参数type:SOCK_STREAM(tcp),SOCK_DGRAM(udp)
//第三个参数使用的协议:protocol IPPROTO_TCP IPPROTO_UDP
if (INVALID_SOCKET == sock)
{
cout << "socket error" << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
else
{
cout << "socket success" << endl;
}
sockaddr_in soserver;
soserver.sin_family = AF_INET;
soserver.sin_addr.S_un.S_addr = inet_addr("10.56.57.156");
soserver.sin_port = htons(54321);
char senbuf[1024] = "";
char revbuf[1024] = "";
//3.链接
int a=connect(sock, (SOCKADDR*)&soserver, sizeof(soserver));
if (a == 0)
{
cout << "connect success" << endl;
}
else
{
cout << "connect error" << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return 1;
}
while (true)
{
//4.发送数据
gets_s(senbuf);
send(sock,senbuf,sizeof(senbuf),0);
if (a == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 1;
}
//5.接收数据
int b = recv(sock, revbuf, sizeof(revbuf), 0);
if (b > 0)
{
printf("Bytes received: %d\n", b);
cout << "服务器发送了:" << revbuf << endl;
}
else if (b == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
}
//6.关闭套接字和卸载库
closesocket(sock);
WSACleanup();
return 0;
}
版权声明:本文为apple_51801179原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。