0 前言
记录一下简易的客户端-服务端的Socket通信。 MFC版放在下一篇。
文末附参考文献及引申阅读。 文中代码直接复制即可运行。 也可后台回复「20210819」,获取源代码工程文件,点击**.sln**即可运行。
1 客户端程序测试(connect)
先从MSDN官方例子开始,抠出通信模型。 在搜索引擎上输入“msdn connect”,查看connect()函数的说明。 链接:connect function (winsock2.h) - Win32 apps | Microsoft Docs 复制“Example Code”代码,在VS新建程序,如下。
1.1 connect代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#ifndef UNICODE
#define UNICODE
#endif
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "ws2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup function failed with error: %d\n", iResult);
return 1;
}
SOCKET ConnectSocket;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
sockaddr_in clientService;
std::string serverIP = "127.0.0.1";
unsigned short serverPORT = 27015;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr(serverIP.c_str());
clientService.sin_port = htons(serverPORT);
iResult = connect(ConnectSocket, (SOCKADDR *)& clientService, sizeof(clientService));
if (iResult == SOCKET_ERROR) {
printf("connect function failed with error: %ld\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
printf("closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
printf("Connected to server.\n");
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR) {
printf("closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
1.2 运行结果
将端口号“27015”改成“666”,运行结果如下:连接失败。
1.3 客户端简易模型
抠出客户端简易模型(不含收发信息): 创建Socket→填写sockaddr_in结构体→connect()连接
1.4 客户端测试(默认端口号记录)
①HTTP服务器,默认的端口号为80/tcp(木马Executor开放此端口); ②HTTPS(securely transferring web pages)服务器,默认的端口号为443/tcp 443/udp;
在控制台中输入“ping www.baidu.com”,获得网站的IP地址,并替换“127.0.0.1”。 IP地址:182.61.200.7 端口号:443 运行结果,连接成功。
1.5 【无】127.0.0.1和27015
1.6 不存在从string到从const char*的适当转换函数
修改:clientService.sin_addr.s_addr = inet_addr(serverIP.c_str());
2 服务端(bind)
接第1节,输入“bind”,搜索查看bind()函数相关信息。 链接:bind function (winsock.h) - Win32 apps | Microsoft Docs
2.1 bind()服务端代码
修改后,最终代码如下:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#ifndef UNICODE
#define UNICODE
#endif
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
int main(int argc, char* argv[])
{
int iResult = 0;
WSADATA wsaData;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("Error at WSAStartup()\n");
return 1;
}
SOCKET ListenSocket = INVALID_SOCKET;
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
printf("socket function failed with error: %u\n", WSAGetLastError());
WSACleanup();
return 1;
}
sockaddr_in service;
std::string serverIP = "127.0.0.1";
unsigned short serverPORT = 666;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(serverIP.c_str());
service.sin_port = htons(serverPORT);
iResult = bind(ListenSocket, (SOCKADDR *)&service, sizeof(service));
if (iResult == SOCKET_ERROR) {
printf("bind failed with error %u\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
else
printf("bind returned success\n");
WSACleanup();
return 0;
}
2.2 运行结果
2.3 服务端简易模型
服务端简易模型如下:
3 服务端客户端通信:accept()、recv()
3.1 recv()和accpet()
accept function (winsock2.h) - Win32 apps | Microsoft Docs recv function (winsock.h) - Win32 apps | Microsoft Docs 以上两个函数,组成一对。
3.1.1 recv()
最终代码如下:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int main(int argc, char* argv[]) {
const char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
SOCKET ConnectSocket = INVALID_SOCKET;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
struct sockaddr_in clientService;
std::string serverIP = "127.0.0.1";
unsigned short serverPORT = 666;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr(serverIP.c_str());
clientService.sin_port = htons(serverPORT);
iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
printf("Unable to connect to server: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0)
printf("Bytes received: %d\n", iResult);
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while (iResult > 0);
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
3.1.2 accept()
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#ifndef UNICODE
#define UNICODE
#endif
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed with error: %ld\n", iResult);
return 1;
}
SOCKET ListenSocket;
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
sockaddr_in service;
std::string serverIP = "0.0.0.0";
unsigned short serverPORT = 666;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(serverIP.c_str());
service.sin_port = htons(serverPORT);
iResult = bind(ListenSocket, (SOCKADDR *)& service, sizeof(service));
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
iResult = listen(ListenSocket, 6);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
SOCKET AcceptSocket;
printf("Waiting for client to connect...\n");
AcceptSocket = accept(ListenSocket, NULL, NULL);
if (AcceptSocket == INVALID_SOCKET) {
printf("accept failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
else
printf("Client connected.\n");
closesocket(ListenSocket);
WSACleanup();
return 0;
}
3.2 运行结果
运行结果如下:
3.3 服务端/客户端模型
扣出模型如下: 从运行结果看出,客户端和服务端还不能实现双方互发消息。 接着往下看啦~ 由上面的程序慢慢修改即可。
3.4 const char类型的值不能用于初始化char类型的实体
解决: const char类型的值不能用于初始化char类型的实体_触动人生的博客-CSDN博客 将char类型改为const char即可。
4 一对一收发(非线程)
4.1 一对一通信
简单回顾以下服务端和客户端的创建步骤。 →创建一个服务端程序步骤如下:前4步放在主函数,因为服务器要一直处于监听状态。 ① 创建套接字(socket) ② 填写sockaddr_in结构体 ③ bind()函数绑定套接字 ④ listen()函数监听,设定最大连接数 ⑤ accept()函数接收客户端连接请求,返回对应此次连接的套接字 ⑥ 用返回的套接字和客户端进行通信(send/recv) ⑦ ……返回另一客户端请求 ⑧ 关闭套接字 →创建一个客户端程序步骤如下: (1)创建套接字(socket) (2)填写sockaddr_in结构体 (3)connect()函数向服务端发连接请求 (4)send()和recv()函数与服务器通信 (5)关闭套接字
4.2 代码如下:
4.2.1 客户端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <string>
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
int main(int argc, char* argv[]) {
char sendbuf[DEFAULT_BUFLEN];
int sendbuflen = DEFAULT_BUFLEN;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
SOCKET ConnectSocket = INVALID_SOCKET;
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
struct sockaddr_in clientService;
std::string serverIP = "127.0.0.1";
unsigned short serverPORT = 666;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr(serverIP.c_str());
clientService.sin_port = htons(serverPORT);
iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
printf("Unable to connect to server: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
while (1) {
printf("请输入要发送的信息:");
fgets(sendbuf, DEFAULT_BUFLEN, stdin);
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
iResult = recv(ConnectSocket, recvbuf, (int)strlen(recvbuf), 0);
recvbuf[iResult] = '\0';
if (iResult > 0)
std::cout << "服务端信息:" << recvbuf << "Bytes received :" << iResult << "\n ";
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
}
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
4.2.2 服务端代码
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#ifndef UNICODE
#define UNICODE
#endif
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <string>
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
int main(int argc, char* argv[])
{
char sendbuf[DEFAULT_BUFLEN];
int sendbuflen = DEFAULT_BUFLEN;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed with error: %ld\n", iResult);
return 1;
}
SOCKET ListenSocket;
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
sockaddr_in service;
std::string serverIP = "0.0.0.0";
unsigned short serverPORT = 666;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(serverIP.c_str());
service.sin_port = htons(serverPORT);
iResult = bind(ListenSocket, (SOCKADDR *)& service, sizeof(service));
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
iResult = listen(ListenSocket, 6);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
SOCKET AcceptSocket;
printf("Waiting for client to connect...\n");
AcceptSocket = accept(ListenSocket, NULL, NULL);
if (AcceptSocket == INVALID_SOCKET) {
printf("accept failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
else
printf("Client connected.\n");
while (1) {
iResult = recv(AcceptSocket, recvbuf, (int)strlen(recvbuf), 0);
recvbuf[iResult] = '\0';
if (iResult > 0)
std::cout << "客户端信息:" << recvbuf << "Bytes received : " << iResult << "\n";
else if (iResult == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
printf("请输入要发送的信息:");
fgets(sendbuf, DEFAULT_BUFLEN, stdin);
iResult = send(AcceptSocket, sendbuf, (int)strlen(sendbuf), 0);
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(AcceptSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
}
closesocket(ListenSocket);
WSACleanup();
return 0;
}
4.2.3运行结果如下:
可以看到,服务端和客户端可以相互进行通信。 但是由于将收发消息放在while循环中,所以只能客户端先发一条消息,服务端再回复消息,客户端再发送消息……这样肯定是不行的,如何解决呢?
4.3 【未】字符串终结符’\0’,TCP发送无边界
5 一对多收发(线程)
5.1 线程运行
为了更好的理解线程,学习一下下方的菜鸟教程吧。 线程相关:C++ 多线程 | 菜鸟教程 (runoob.com)
5.2 一对多
如何让服务端和客户端自由收发消息,且服务端能知道是来自哪个客户端的消息。
服务端:将main()函数作为主线程,不断接收客户端的连接请求,即4.1节服务端前4步。再新建子线程,每连接一个客户端,就专为此客户端新建一个用于接收信息并显示到屏幕上功能的子线程。然后,新建子线程,专用于本机发送消息。
客户端:将main()函数作为主线程,连接服务器。新建子线程,用于从服务器接收信息,再新建子线程,用于从客户端向服务器中发送信息。 即:单个服务器的进程利用服务器中的线程与多个客户端进程进行通讯。
5.2.1 程序如下:
5.2.1.1 客户端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
#include <string>
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
#define DEFAULT_BUFLEN 512
SOCKET ConnectSocket = INVALID_SOCKET;
DWORD WINAPI SendMessageThread(LPVOID lpParam);
DWORD WINAPI ReceiveMessageThread(LPVOID lpParam);
int main(int argc, char* argv[]) {
int iResult;
WSADATA wsaData;
char recvbuf[DEFAULT_BUFLEN];
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed with error:%d\n", iResult);
return 1;
}
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
SOCKADDR_IN clientService;
string serverIP = "127.0.0.3";
unsigned short serverPORT = 666;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr(serverIP.c_str());
clientService.sin_port = htons(serverPORT);
iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (iResult == SOCKET_ERROR) {
printf("connect failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, NULL, 0, NULL);
while (1) {
printf("main#001:请输入要发送的消息:\n");
char sendbuf[DEFAULT_BUFLEN];
fgets(sendbuf, DEFAULT_BUFLEN, stdin);
sendbuf[strlen(sendbuf) - 1] = '\0';
if (strlen(sendbuf) == 1 && sendbuf[0] == 'q') {
closesocket(ConnectSocket);
break;
}
std::string sendSTR = sendbuf;
int rtn = send(ConnectSocket, sendSTR.c_str(), sendSTR.length(), 0);
if (rtn == SOCKET_ERROR)
{
printf("main#002:send函数调用失败,错误码=%d;\n", GetLastError());
closesocket(ConnectSocket);
break;
}
}
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR) {
printf("close failed with error: %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
DWORD WINAPI SendMessageThread(LPVOID lpParam) {
while (ConnectSocket != INVALID_SOCKET) {
printf("snd#002:请输入要发送的消息:\n");
char sendbuf[DEFAULT_BUFLEN];
fgets(sendbuf, DEFAULT_BUFLEN, stdin);
sendbuf[strlen(sendbuf) - 1] = '\0';
if (strlen(sendbuf) == 1 && sendbuf[0] == 'q') {
closesocket(ConnectSocket);
break;
}
std::string sendSTR = sendbuf;
int rtn = send(ConnectSocket, sendSTR.c_str(), sendSTR.length(), 0);
if (rtn == SOCKET_ERROR)
{
printf("snd#003:send函数调用失败,错误码=%d;\n", GetLastError());
closesocket(ConnectSocket);
break;
}
}
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
return 0;
}
DWORD WINAPI ReceiveMessageThread(LPVOID lpParam) {
while (ConnectSocket != INVALID_SOCKET) {
char rcvBuf[DEFAULT_BUFLEN];
int rtn = recv(ConnectSocket, rcvBuf, sizeof(rcvBuf) - 1, 0);
if (rtn == 0) {
printf("recv#003:通过调用closesocket函数关闭了TCP连接\n");
break;
}
else if(rtn == SOCKET_ERROR) {
printf("recv#004:rtn = SOCKET_ERROR; TCP连接出错(各种意外因素,如程序线程被杀死、网络通信中断等等),错误码=%d\n", GetLastError());
break;
}
rcvBuf[rtn] = '\0';
printf("Server says: %s\n", rcvBuf);
}
return 0;
}
5.2.1.2 服务端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#ifndef UNICODE
#define UNICODE
#endif
#include <winsock2.h>
#include <stdio.h>
#include <windows.h>
#include <string>
#include <string.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;
#pragma comment(lib, "Ws2_32.lib")
vector <SOCKET> clientSocketGroup;
#define DEFAULT_BUFLEN 512
SOCKET AcceptSocket;
DWORD WINAPI ReceiveMessageThread(LPVOID lpParam);
DWORD WINAPI SendMessageThread(LPVOID IpParameter);
typedef struct client_info {
SOCKET cSocket;
char IP[128];
unsigned short Port;
}CLIENT_INFO;
int main(int argc, char* argv[]) {
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed with error: %ld\n", iResult);
return 1;
}
SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
SOCKADDR_IN service;
SOCKADDR_IN clientAddr;
CLIENT_INFO cINFO;
std::string serverIP = "0.0.0.0";
unsigned short serverPORT = 666;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(serverIP.c_str());
service.sin_port = htons(serverPORT);
iResult = bind(ListenSocket, (SOCKADDR *)& service, sizeof(service));
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
iResult = listen(ListenSocket, 6);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
while (1) {
int ClientAddrBytes = sizeof(sockaddr_in);
AcceptSocket = accept(ListenSocket, (sockaddr *)&clientAddr, &ClientAddrBytes);
if (AcceptSocket == INVALID_SOCKET) {
printf("accept failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
else {
printf("Client connected.\n");
clientSocketGroup.push_back(AcceptSocket);
}
printf("main#001: 收到客户端TCP连接:%s:%d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
cINFO.cSocket = AcceptSocket;
strncpy(cINFO.IP, inet_ntoa(clientAddr.sin_addr), 16);
cINFO.Port = ntohs(clientAddr.sin_port);
printf("main#002: 仍有[%d]个客户端服务线程在运行\n", clientSocketGroup.size());
CLIENT_INFO *pNewClient = new CLIENT_INFO;
memcpy(pNewClient, &cINFO, sizeof(CLIENT_INFO));
HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, pNewClient, 0, NULL);
if (receiveThread == NULL){
printf("error#174:创建线程失败,错误码=%d; 按下回车退出!\n", GetLastError());
closesocket(ListenSocket);
return -1;
}
HANDLE sendThread = CreateThread(NULL, 0, SendMessageThread, NULL, 0, NULL);
}
WSACleanup();
return 0;
}
DWORD WINAPI ReceiveMessageThread(LPVOID lpParam) {
char recvbuf[DEFAULT_BUFLEN];
CLIENT_INFO uCLIENT;
memcpy(&uCLIENT, lpParam, sizeof(CLIENT_INFO));
delete lpParam;
while (uCLIENT.cSocket != INVALID_SOCKET) {
int rtn = recv(uCLIENT.cSocket, recvbuf, sizeof(recvbuf)-1, 0);
if (rtn == 0){
printf("recv#003: Recv线程:rtn = 0; 表达对端优雅地关闭了TCP连接(通过调用closesocket函数)!\n");
break;
}
else if (rtn == SOCKET_ERROR){
printf("recv#004: Recv线程:rtn = SOCKET_ERROR; TCP连接出错(各种意外因素,如程序线程被杀死、网络通信中断等等),错误码=%d\n", GetLastError());
break;
}
recvbuf[rtn] = '\0';
printf("Client says: %s,==%s:%d消息[%d]字节==\n", recvbuf, uCLIENT.IP, uCLIENT.Port, rtn);
}
closesocket(uCLIENT.cSocket);
uCLIENT.cSocket = INVALID_SOCKET;
return 0;
}
DWORD WINAPI SendMessageThread(LPVOID IpParameter) {
while (1) {
char sendbuf[DEFAULT_BUFLEN];
fgets(sendbuf, DEFAULT_BUFLEN, stdin);
for (int i = 0; i < clientSocketGroup.size(); ++i) {
std::string sndSTR = sendbuf;
send(clientSocketGroup[i], sndSTR.c_str(), DEFAULT_BUFLEN, 0);
}
}
return 0;
}
5.1.2.3 运行结果
运行结果如下: 从上图可以看出,客户端会反复说:Server says:,在另一组通信中却不会(后期再改),如下: 下一步:服务端不仅能广播消息给每个客户端,还能发送信息给某个指定的客户端。一次发出的消息超过DEFAULT_BUFFER时,会发生什么呢?能正确接收到消息吗?
参考文献【未贴完,今天寝室要关门了】
- 导师的课程代码及MSDN官方代码
- https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect
|