多线程聊天室
- 工作环境: vs2019;
- 原理:利用套接字编程,通过c代码实现;采用TCP协议。
- 目的:客户机可以向服务器发送信息(服务器向客户机发送信息本程序暂未实现),可以多客户机向服务器发信息。
单线程服务器模块
1.SOCKET建立套接字
确定版本信息
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (HIBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2) {
printf("确定版本失败:%d\n", GetLastError());
return -1;
}
printf("确定版本成功\n");
建立流套接字
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == SOCKET_ERROR) {
printf("创建socket失败:%d\n", GetLastError());
WSACleanup();
return -1;
}
printf("确定socket成功\n");
头文件(所有)
#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#include<windows.h>
#include<WS2tcpip.h>
2.设置服务器地址族
SOCKADDR_IN addr = { 0 };
addr.sin_family = AF_INET;//必须与服务器的Socket中地址族相同
inet_pton(AF_INET, "192.168.1.29", (void*)&addr.sin_addr.S_un.S_addr);
//addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.29");//IP地址,把字符串变为整形,即点分十进制的IP变为网络字节顺序的IP地址
addr.sin_port = htons(9527);//端口号
使用inet_addr(),vs会报错。由于在vs2013以后的版本中,增加了inet_pton()、InetPton()之类的新函数,用于IP地址在点分十进制和二进制整数之间转换,并且可以处理IPV4和IPV6,而inet_addr()是老函数,高版本vs在编译时默认使用了新函数,所以会报错。(转)
若使用inet_pton(),增加头文件,#include<WS2tcpip.h>。
否则,修改VS配置,告诉它我要旧函数:项目->c/c+±>常规->SDL检查,将‘是’改成‘否’。(转)
IP地址,为本主机连网时的IP地址,可以cmd(打开命令提示符)->ipconfig,即可查询。
3.绑定套接字和本地地址
int r = bind(serverSocket, (SOCKADDR*)&addr, sizeof(SOCKADDR));
if (r == -1) {
printf("绑定失败:%d\n", GetLastError());
closesocket(serverSocket);//断开连接
WSACleanup();//清除版本信息
return -1;
}
printf("绑定成功\n");
4.监听
r = listen(serverSocket, 10);
if (r == -1) {
printf("监听失败:%d\n", GetLastError());
closesocket(serverSocket);//断开连接
WSACleanup();//清除版本信息
return -1;
}
printf("监听成功\n");
5.接受连接
SOCKADDR_IN addr_1 = { 0 };
int len = sizeof(addr_1);
SOCKET clientSocket= accept(serverSocket, (SOCKADDR*)&addr_1, &len);
if (clientSocket == SOCKET_ERROR) {
printf("服务器崩溃:%d\n", GetLastError());
closesocket(serverSocket);//断开连接
WSACleanup();//清除版本信息
return -1;
}
//printf("客户端连接服务器成功:%s\n",inet_ntoa(addr_1.sin_addr));
char buf[20] = { "0" };
printf("客户端连接服务器成功:%s\n", inet_ntop(AF_INET,(void*)&addr_1.sin_addr,buf,16));
6.接收数据
char buffer[1024];
while (1) {
r = recv(clientSocket, buffer, 1023, NULL);
if (r > 0) {
buffer[r]=0;//添加结束符,否则出现乱码
printf("来自客户端信息:%s\n", buffer);
}
}
7.结果:
在文件里的debug中,运行单线程服务器.exe,如图:
运行客户机之后,输入发送的数据,结果如图(服务器-客户机):
客户机模块
建立套接字、connect()连接、发送数据。
char buffer[1024];
while (1) {
//编译器提示scanf不安全,提倡使用scanf_s
printf("请输入: ");
scanf_s("%s", buffer,1023);
send(clientSocket, buffer, strlen(buffer),NULL);
}
其他与服务器类似
多线程服务器模块
利用线程实现,CreateThread();
设置客户机数组,在利用for循环接收数据。
//6.接收连接
SOCKADDR_IN addr_1 = { 0 };
int len = sizeof(addr_1);
for (int i = 0; i < CLIENT_NUM; i++) {
clientSocket[i] = accept(serverSocket, (SOCKADDR*)&addr_1, &len);
if (clientSocket[i] == SOCKET_ERROR) {
printf("服务器崩溃:%d\n", GetLastError());
closesocket(serverSocket);//断开连接
WSACleanup();//清除版本信息
return -1;
}
//printf("客户端连接服务器成功:%s\n",inet_ntoa(addr_1.sin_addr));
char buf[20] = { "0" };
printf("客户端%d连接服务器成功:%s\n",i+1, inet_ntop(AF_INET, (void*)&addr_1.sin_addr, buf, 16));
CreateThread(NULL,NULL,LPTHREAD_START_ROUTINE(func),(void *)i,NULL,NULL);//LPVOID即 void *
}
截图:
小结
inet_pton,inet_ntop()参考了其他人的建议,代码是通过b站学习的。
希望对大家有帮助!!