• CC++ Socket - TCP 与 UDP 网络编程


    前言

    socket编程分为TCP和UDP两个模块,其中TCP是可靠的、安全的,常用于发送文件等,而UDP是不可靠的、不安全的,常用作视频通话等。

    如下图:
    在这里插入图片描述

    头文件与库:

    #include 
    
    #pragma comment(lib, "ws2_32.lib")
    
    • 1
    • 2
    • 3

    准备工作:

    创建工程后,首先右键工程,选择属性
    在这里插入图片描述

    然后选择 C/C++ - 预处理器 - 预处理器定义

    在这里插入图片描述

    将字符串 _WINSOCK_DEPRECATED_NO_WARNINGS 添加到里面去,点击应用即可!


    TCP

    连接过程图:
    在这里插入图片描述

    创建tcp服务器和客户端都是按照上图的步骤来操作的!

    1). 服务器

    1. 初始化套接字库
      对应图中socket()

      WORD wVersion;
      WSADATA wsaData;
      int err;
      
      // 设置版本,可以理解为1.1
      wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
      
      // 启动
      err = WSAStartup(wVersion, &wsaData);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    2. 创建tcp套接字
      对应图中socket()

      // AF_INET:ipv4   AF_INET6:ipv6
      SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
      
      • 1
      • 2
    3. 绑定到本机
      对应图中bind()

      // 准备绑定信息
      SOCKADDR_IN addrSrv;
      addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	// 设置绑定网卡
      addrSrv.sin_family = AF_INET;		// 设置绑定网络模式
      addrSrv.sin_port = htons(6000);		// 设置绑定端口
      // hton: host to network  x86:小端    网络传输:htons大端
      
      // 绑定到本机
      int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    4. 监听
      对应图中listen()

      // 同时能接收10个链接,主要看参数二的设置个数
      listen(sockSrv, 10);
      
      • 1
      • 2
    5. 接收连接请求,返回针对客户端的套接字
      对应图中accept()

      SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
      
      • 1
    6. 发送数据
      对应图中write()

      sprintf_s(sendBuf, "hello client!
      ");
      int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
      
      • 1
      • 2
      • 3
    7. 接收数据
      对应图中read()

      recv(sockConn, recvBuf, 100, 0);
      
      • 1
    8. 关闭套接字
      对应图中close()

      closesocket(sockConn);
      
      • 1
    9. 清理套接字库

      WSACleanup();
      
      • 1

    具体实现代码

    #include 
    #include 
    #include 
    
    #pragma comment(lib, "ws2_32.lib")
    
    
    int main(void) {
    
    	// 1.初始化套接字库
    	WORD wVersion;
    	WSADATA wsaData;
    	int err;
    
    	// 设置版本,可以理解为1.1
    	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
    
    	// 启动
    	err = WSAStartup(wVersion, &wsaData);
    	if (err != 0) {
    		return err;
    	}
    	// 检查:网络低位不等于1 || 网络高位不等于1
    	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    		// 清理套接字库
    		WSACleanup();
    		return -1;
    	}
    
    	// 2.创建tcp套接字		// AF_INET:ipv4   AF_INET6:ipv6
    	SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    
    	// 准备绑定信息
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	// 设置绑定网卡
    	addrSrv.sin_family = AF_INET;		// 设置绑定网络模式
    	addrSrv.sin_port = htons(6000);		// 设置绑定端口
    	// hton: host to network  x86:小端    网络传输:htons大端
    
    	// 3.绑定到本机
    	int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	if (retVal == SOCKET_ERROR) {
    		printf("Failed bind:%d
    ", WSAGetLastError());
    		return -1;
    	}
    
    	// 4.监听,同时能接收10个链接
    	if (listen(sockSrv, 10) == SOCKET_ERROR) {
    		printf("Listen failed:%d", WSAGetLastError());
    		return -1;
    	}
    
    	std::cout << "Server start at port: 6000" << std::endl;
    
    	SOCKADDR_IN addrCli;
    	int len = sizeof(SOCKADDR);
    
    	char recvBuf[100];
    	char sendBuf[100];
    	while (1) {
    		// 5.接收连接请求,返回针对客户端的套接字
    		SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
    		if (sockConn == SOCKET_ERROR) {
    			//printf("Accept failed:%d", WSAGetLastError());
    			std::cout << "Accept failed: " << WSAGetLastError() << std::endl;
    			break;
    		}
    
    		//printf("Accept client IP:[%s]
    ", inet_ntoa(addrCli.sin_addr));
    		std::cout << "Accept client IP: " << inet_ntoa(addrCli.sin_addr) << std::endl;
    
    		// 6.发送数据
    		sprintf_s(sendBuf, "hello client!
    ");
    		int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
    		if (iSend == SOCKET_ERROR) {
    			std::cout << "send failed!
    ";
    			break;
    		}
    
    		// 7.接收数据
    		recv(sockConn, recvBuf, 100, 0);
    		std::cout << recvBuf << std::endl;
    
    		// 关闭套接字
    		closesocket(sockConn);
    	}
    
    
    	// 8.关闭套接字
    	closesocket(sockSrv);
    
    	// 9.清理套接字库
    	WSACleanup();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    2). 客户端

    1. 初始化套接字库
      对应图中socket()

      WORD wVersion;
      WSADATA wsaData;
      int err;
      
      // 可以理解为1.1
      wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
      
      // 启动
      err = WSAStartup(wVersion, &wsaData);
      
      // 创建TCP套接字
      SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    2. 连接服务器
      对应图中connect()

      // 连接服务器
      int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
      
      • 1
      • 2
    3. 发送数据到服务器
      对应图中write()

      char sendBuf[] = "你好,服务器,我是客户端!";
      send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
      
      • 1
      • 2
    4. 接收服务器的数据
      对应图中read()

      char recvBuf[100];
      recv(sockCli, recvBuf, sizeof(recvBuf), 0);
      
      • 1
      • 2
    5. 关闭套接字并清除套接字库
      对应图中close()

      closesocket(sockCli);
      WSACleanup();
      
      • 1
      • 2

    具体实现代码

    #include 
    #include 
    
    
    #pragma comment(lib, "ws2_32.lib")
    
    
    int main(void) {
    
    	// 1.初始化套接字库
    	WORD wVersion;
    	WSADATA wsaData;
    	int err;
    
    	// 可以理解为1.1
    	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
    
    	// 启动
    	err = WSAStartup(wVersion, &wsaData);
    	if (err != 0) {
    		return err;
    	}
    	// 检查:网络地位不等于1 || 网络高位不等于1
    	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    		// 清理套接字库
    		WSACleanup();
    		return -1;
    	}
    
    	// 创建TCP套接字
    	SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");	// 服务器地址
    	addrSrv.sin_port = htons(6000);		// 端口号
    	addrSrv.sin_family = AF_INET;		// 地址类型(ipv4)
    
    	// 2.连接服务器
    	int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	if (err_log == 0) {
    		printf("连接服务器成功!
    ");
    	
    	} else {
    		printf("连接服务器失败!
    ");
    		return -1;
    	}
    
    
    	char recvBuf[100];
    	char sendBuf[] = "你好,服务器,我是客户端!";
    	// 3.发送数据到服务器
    	send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
    
    	// 4.接收服务器的数据
    	recv(sockCli, recvBuf, sizeof(recvBuf), 0);
    	std::cout << recvBuf << std::endl;
    
    
    	// 5.关闭套接字并清除套接字库
    	closesocket(sockCli);
    	WSACleanup();
    
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    运行效果:
    在这里插入图片描述

    3). TCP聊天小项目

    下面是根据上面的代码修改的一个聊天小项目(使用到了多线程)

    只有一个服务器,服务器一直开启等待客户端连接;
    客户都安可以开启多个,且可以一直连续的与服务器进行发送接收消息;
    服务器给客户端发送数据,得通过1 - 9来区分到底给那个客户端发送消息,例如给第二个客户端发送消息:2你好,客户端
    客户端那边接收到的数据是:你好,客户端

    服务器代码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #pragma comment(lib, "ws2_32.lib")
    
    
    SOCKET sockSrv;
    std::vector vec_sockConn;
    std::vector vec_sockaddr_in;
    std::vector vec_sockIndex;
    
    
    // 这个结构体用作线程参数
    typedef struct SERVER_CLIENT {
    	SOCKET server;
    	SOCKADDR_IN client;
    	int clientIndex;
    }SC;
    
    
    
    // 判断有没有断开连接
    bool IsSocketClosed(SOCKET clientSocket) {
    	bool ret = false;
    	HANDLE closeEvent = WSACreateEvent();
    	WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);
    
    	DWORD dwRet = WaitForSingleObject(closeEvent, 0);
    
    	if (dwRet == WSA_WAIT_EVENT_0)
    		ret = true;
    	else if (dwRet == WSA_WAIT_TIMEOUT)
    		ret = false;
    
    	WSACloseEvent(closeEvent);
    	return ret;
    }
    
    
    // 接收请求
    unsigned int WINAPI  ThreadAccept(LPVOID p) {
    	static int i = 0;
    	while (1) {
    		SOCKADDR_IN addrCli;
    		int len = sizeof(SOCKADDR);
    
    		// 5.接收连接请求,返回针对客户端的套接字
    		SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);
    		if (sockConn == SOCKET_ERROR) {
    			printf("Accept failed:%d", WSAGetLastError());
    		}
    
    		// 存储当前服务器与客户端 连接绑定的socket
    		vec_sockIndex.emplace_back(i++);
    		vec_sockaddr_in.emplace_back(addrCli);
    		vec_sockConn.emplace_back(sockConn);
    
    		printf("[0;%d;40m客户端[%d]上线[0m
    ", 31, i);
    	}
    	
    
    	return 0;
    }
    
    unsigned int WINAPI  _ThreadRecv(LPVOID p) {
    	char recvBuf[100];
    	memset(recvBuf, 0, 100);
    
    	SC _sc = *(SC *)p;
    	
    	while (1) {
    		Sleep(20);
    
    		if (IsSocketClosed(_sc.server) == true) {
    			printf("客户端 [%d] 断开连接!
    ", _sc.clientIndex + 1);
    			break;
    		}
    
    
    		// 接收数据
    		recv(_sc.server, recvBuf, 100, 0);
    		if (strlen(recvBuf) == 0) {
    			continue;
    		}
    	
    		printf("接收到客户端 [%d] 的消息:%s
    ", _sc.clientIndex + 1, recvBuf);
    		memset(recvBuf, 0, 100);
    	}
    	return 0;
    }
    
    unsigned int WINAPI  ThreadRecv(LPVOID p) {
    
    	static int index = 0;
    
    	while (1) {
    		// 还没有客户端与服务器进行连接
    		if (vec_sockConn.size() == 0) {
    			continue;
    		}
    
    		// 接收线程已经开启和客户端个数相等
    		if (vec_sockConn.size()  == index) {
    			continue;
    		}
    
    		SC sc;
    		sc.server = vec_sockConn.at(index);
    		sc.client = vec_sockaddr_in.at(index);
    		sc.clientIndex = vec_sockIndex.at(index);
    
    		HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadRecv, (void *)&sc, 0, NULL);
    
    		index++;
    		Sleep(20);
    	}
    
    	return 0;
    }
    
    
    
    int main(void) {
    
    	// 1.初始化套接字库
    	WORD wVersion;
    	WSADATA wsaData;
    	int err;
    
    	// 设置版本,可以理解为1.1
    	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
    
    	// 启动
    	err = WSAStartup(wVersion, &wsaData);
    	if (err != 0) {
    		return err;
    	}
    	// 检查:网络低位不等于1 || 网络高位不等于1
    	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    		// 清理套接字库
    		WSACleanup();
    		return -1;
    	}
    
    	// 2.创建tcp套接字		// AF_INET:ipv4   AF_INET6:ipv6
    	sockSrv = socket(AF_INET, SOCK_STREAM, 0);
    
    	// 准备绑定信息
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	// 设置绑定网卡
    	addrSrv.sin_family = AF_INET;		// 设置绑定网络模式
    	addrSrv.sin_port = htons(6000);		// 设置绑定端口
    	// hton: host to network  x86:小端    网络传输:htons大端
    
    	// 3.绑定到本机
    	int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	if (retVal == SOCKET_ERROR) {
    		printf("Failed bind:%d
    ", WSAGetLastError());
    		return -1;
    	}
    
    	// 4.监听,同时接收10个链接
    	if (listen(sockSrv, 10) == SOCKET_ERROR) {
    		printf("Listen failed:%d", WSAGetLastError());
    		return -1;
    	}
    
    	std::cout << "Server start at port: 6000" << std::endl;
    
    
    	// 线程句柄				// 创建线程
    	HANDLE hThread_1 = (HANDLE)_beginthreadex(NULL, 0, ThreadAccept, NULL, 0, NULL);
    	HANDLE hThread_2 = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, NULL, 0, NULL);
    
    
    	//uiInit();
    	//editPrint(0, ">");
    
    
    	char sendBuf[100];
    	while (1) {
    
    		//printf("请输入发送内容:");
    
    		char c = getchar();				// 输入发送给谁
    		scanf_s("%s", sendBuf, 100);	// 输入发送的内容
    
    		if (strlen(sendBuf) == 0) {
    			printf("输入内容为空或者超长!
    ");
    		}
    
    		// 1 至 9
    		if (c < '1' || c > '9' || vec_sockConn.size() == 0 || c - '0' >= vec_sockConn.size() + 1) {
    			while ((c = getchar()) != '
    ');	// 清空输入缓冲区
    			memset(sendBuf, 0, 100);
    			printf("输入内容不符合规则!
    ");
    			continue;
    		}
    
    
    
    		// 发送数据
    		int index = --c - '0';	// 因为下标是从零开始的,所以c要先自减
    		int iSend = send(vec_sockConn.at(index) , sendBuf, strlen(sendBuf) + 1, 0);
    		if (iSend == SOCKET_ERROR) {
    			std::cout << "send failed!
    ";
    			break;
    		}
    
    
    		memset(sendBuf, 0, 100);
    		while ((c = getchar()) != '
    ');	// 清空输入缓冲区
    	}
    
    	// 关闭套接字
    	std::vector::iterator it = vec_sockConn.begin();
    	for (; it != vec_sockConn.end(); it++) {
    		closesocket((SOCKET)(*it));
    	}
    
    
    	WaitForSingleObject(hThread_1, INFINITE);
    	WaitForSingleObject(hThread_2, INFINITE);
    
    	CloseHandle(hThread_1);
    	CloseHandle(hThread_2);
    
    	// 7.关闭套接字
    	closesocket(sockSrv);
    
    	// 8.清理套接字库
    	WSACleanup();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251

    客户端

    #include 
    #include 
    #include 
    #include 
    
    #pragma comment(lib, "ws2_32.lib")
    
    SOCKET sockCli;
    
    
    // 判断有没有断开连接
    bool IsSocketClosed(SOCKET clientSocket) {
    	bool ret = false;
    	HANDLE closeEvent = WSACreateEvent();
    	WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);
    
    	DWORD dwRet = WaitForSingleObject(closeEvent, 0);
    
    	if (dwRet == WSA_WAIT_EVENT_0)
    		ret = true;
    	else if (dwRet == WSA_WAIT_TIMEOUT)
    		ret = false;
    
    	WSACloseEvent(closeEvent);
    	return ret;
    }
    
    
    unsigned int WINAPI  ThreadRecv(LPVOID p) {
    	char recvBuf[100];
    	memset(recvBuf, 0, 100);
    
    	while (1) {
    		Sleep(20);
    
    		if (IsSocketClosed(sockCli) == true) {
    			printf("服务器 断开连接!
    ");
    			break;
    		}
    
    		// 接收服务器的数据
    		recv(sockCli, recvBuf, sizeof(recvBuf), 0);
    		if (strlen(recvBuf) == 0) continue;
    
    		std::cout << recvBuf << std::endl;
    		memset(recvBuf, 0, 100);
    	}
    
    
    	return 0;
    }
    
    int main(void) {
    
    	// 1.初始化套接字库
    	WORD wVersion;
    	WSADATA wsaData;
    	int err;
    
    	// 可以理解为1.1
    	wVersion = MAKEWORD(1, 1);	// 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来
    
    	// 启动
    	err = WSAStartup(wVersion, &wsaData);
    	if (err != 0) {
    		return err;
    	}
    	// 检查:网络地位不等于1 || 网络高位不等于1
    	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    		// 清理套接字库
    		WSACleanup();
    		return -1;
    	}
    
    	// 创建TCP套接字
    	sockCli = socket(AF_INET, SOCK_STREAM, 0);
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");	// 服务器地址
    	addrSrv.sin_port = htons(6000);		// 端口号
    	addrSrv.sin_family = AF_INET;		// 地址类型(ipv4)
    
    	// 连接服务器
    	int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    	if (err_log == 0) {
    		printf("连接服务器成功!
    ");
    
    	} else {
    		printf("连接服务器失败!
    ");
    		return -1;
    	}
    
    	// 线程句柄				// 创建线程
    	HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, NULL, 0, NULL);
    
    
    	char sendBuf[100];
    	while (1) {
    
    		//printf("请输入发送内容:");
    		scanf_s("%s", sendBuf, 100);
    
    		// 发送数据到服务器
    		send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);
    
    		memset(sendBuf, 0, 100);
    		char c;
    		while ((c = getchar()) != '
    ');
    	}
    
    
    	WaitForSingleObject(hThread, INFINITE);
    	CloseHandle(hThread);
    
    
    	// 关闭套接字并清除套接字库
    	closesocket(sockCli);
    	WSACleanup();
    
    	system("pause");
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    运行效果:
    在这里插入图片描述


    UDP

    UDP就比较简单了,步骤比tcp要少一些。

    连接过程图:
    在这里插入图片描述

    1). 服务器

    1. 初始化套接字库

      WORD wVersion;
      WSADATA wsaData;
      int err;
      
      wVersion = MAKEWORD(1, 1);
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. 创建套接字

      SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
      
      • 1
    3. 绑定

      // SOCKADDR_IN addrSrv; 省略了定义和赋值
      bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
      
      • 1
      • 2
    4. 接收数据

      char recvBuf[100];
      recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);
      
      • 1
      • 2
    5. 发送数据

      char sendBuf[] = "hello Client,I'm Server!
      ";
      sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrCli, len);
      
      • 1
      • 2
      • 3
    6. 关闭

      closesocket(sockSrv);
      WSACleanup();
      
      • 1
      • 2

    具体实现代码

    #include 
    #include 
    
    #pragma comment(lib, "ws2_32.lib")
    
    int main(void) {
    	// 初始化套接字库
    	WORD wVersion;
    	WSADATA wsaData;
    	int err;
    
    	wVersion = MAKEWORD(1, 1);
    	err = WSAStartup(wVersion, &wsaData);
    	if (err != 0) {
    		return err;
    	}
    
    	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    		WSACleanup();
    		return -1;
    	}
    
    	// 创建套接字
    	SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
    
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_port = htons(6001);
    
    	// 绑定到本机6001端口
    	bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
    
    	// 接收请求,处理请求
    	SOCKADDR_IN addrCli;
    	int len = sizeof(SOCKADDR);
    
    	char sendBuf[] = "hello Client,I'm Server!
    ";
    	char recvBuf[100];
    
    	std::cout << "start UDP server with port 6001" << std::endl;
    	while (1) {
    		// 接收数据
    		recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);
    		std::cout << "Recv:" << recvBuf << std::endl;
    
    		// 发送数据
    		sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrCli, len);
    		std::cout << "Send:" << sendBuf << std::endl;
    	}
    
    	closesocket(sockSrv);
    	WSACleanup();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    2). 客户端

    1. 初始化套接字库

      WORD wVersion;
      WSADATA wsaData;
      int err;
      
      wVersion = MAKEWORD(1, 1);
      
      • 1
      • 2
      • 3
      • 4
      • 5
    2. 创建UDP套接字

      SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
      SOCKADDR_IN addrSrv;
      
      • 1
      • 2
    3. 接收数据

      char recvBuf[100];
      recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);
      
      • 1
      • 2
    4. 发送数据

      char sendBuf[] = "hello Client,I'm Server!
      ";
      sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrSrv, len);
      
      • 1
      • 2
      • 3
    5. 关闭

      closesocket(sockSrv);
      WSACleanup();
      
      • 1
      • 2

    具体实现代码

    #include 
    #include 
    
    #pragma comment(lib, "ws2_32.lib")
    
    
    int main(void) {
    
    	// 初始化套接字库
    	WORD wVersion;
    	WSADATA wsaData;
    	int err;
    
    	wVersion = MAKEWORD(1, 1);
    	err = WSAStartup(wVersion, &wsaData);
    	if (err != 0) {
    		return err;
    	}
    
    	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
    		WSACleanup();
    		return -1;
    	}
    
    	// 创建UDP套接字
    	SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
    	SOCKADDR_IN addrSrv;
    	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    	addrSrv.sin_family = AF_INET;
    	addrSrv.sin_port = htons(6001);
    
    	SOCKADDR_IN addrCli;
    	int len = sizeof(SOCKADDR);
    
    	char sendBuf[] = "hello, I'm Client!
    ";
    	char recvBuf[100];
    
    	std::cout << "send to Server: " << sendBuf << std::endl;
    	sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrSrv, len);
    
    	recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);
    	std::cout << "recv from: " << recvBuf << std::endl;
    
    
    	closesocket(sockCli);
    	WSACleanup();
    
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    运行效果:
    在这里插入图片描述


    总结

    socket的具体细节用法我不太清楚,现阶段也只是熟悉TCP的一些简单操作,UDP的话也还不是太懂,不懂的是不知道在具体项目中该如何进行使用它们。
    那个TCP的小项目也只是自己琢磨搞出来的,不知掉具体项目会不会这样去写!

  • 相关阅读:
    研一R语言选修课程第二次课后习题分享(含代码)
    CK98-数学家键盘配置
    spring cloud config使用与原理分析
    java计算机毕业设计HTML5互动游戏新闻网站设计与实现源码+mysql数据库+系统+lw文档+部署
    NCP81239MNTXG 开关降压/升压控制器,USB 功率传递和 Type-C 应用
    #define的使用
    Maven编程环境搭建以及VS code Maven设置
    最新Jn建站系统2.0 已集成各类源码 【附视频安装教程】
    成都瀚网科技有限公司:抖音小店选品策略引领电商潮流
    甘露糖-聚赖氨酸|PLL-PEG-mannose|聚赖氨酸-PEG-甘露糖
  • 原文地址:https://blog.csdn.net/m0_67394002/article/details/126326513