Windows下Socket服务器同时连接多客户端
https://blog.csdn.net/yao_hou/article/details/102728970
下面的代码是一个server对应多个client, 仅做测试demo, 如果是实际项目,需实际处理。
编译脚本CMakeList.txt
如果使用cmake编译,需要添加ws2_32库
link_libraries(ws2_32)
add_executable(select_client main.cpp)
服务端代码
服务器单线程启动,监听8000端口
#include <winsock2.h>
#include <iostream>
using namespace std;
int main() {
// 初始化winsock的环境
WSADATA wd;
if (WSAStartup(MAKEWORD(2, 2), &wd) == SOCKET_ERROR) {
cout << "WSAStartup error:" << GetLastError() << endl;
return 0;
}
// 1.创建监听套接字
SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sListen == INVALID_SOCKET) {
cout << "socket error:" << GetLastError() << endl;
return 0;
}
// 2.绑定到ip与端口
sockaddr_in serverSockAddr;
serverSockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
serverSockAddr.sin_port = htons(8000);
serverSockAddr.sin_family = AF_INET;
int len = sizeof(sockaddr_in);
if (bind(sListen, (SOCKADDR *) &serverSockAddr, len) == SOCKET_ERROR) {
cout << "bind error:" << GetLastError() << endl;
return 0;
}
// 3.监听套接字
if (listen(sListen, 5) == SOCKET_ERROR) {
cout << "listen error:" << GetLastError() << endl;
return 0;
}
// 4. select开始了
fd_set readSet;//定义一个读(接受消息)的集合
FD_ZERO(&readSet);//初始化集合
FD_SET(sListen, &readSet);
// 不停的select才可以读取套接字的状态改变
while (true) {
fd_set tmpSet; // 定义一个临时的集合
FD_ZERO(&tmpSet); // 初始化集合
tmpSet = readSet; // 每次循环都是所有的套接字
// 利用select选择出集合中可以读写的多个套接字,有点像筛选
int ret = select(0, &tmpSet, NULL, NULL, NULL);//最后一个参数为NULL,一直等待,直到有数据过来
if (ret == SOCKET_ERROR) {
continue;
}
// 成功筛选出来的tmpSet可以发送或者接收的socket
for (int i = 0; i < tmpSet.fd_count; ++i) {
//获取到套接字
SOCKET selectedSocket = tmpSet.fd_array[i];
// 接收到客户端的链接
if (selectedSocket == sListen) {
SOCKET c = accept(selectedSocket, NULL, NULL);
// fd_set集合最大值为64
if (readSet.fd_count < FD_SETSIZE) {
//往集合中添加客户端套接字
FD_SET(c, &readSet);
cout << c << " logged in." << endl;
// 给客户端发送欢迎
char buf[100] = {0};
sprintf(buf, "hello from server", c);
send(c, buf, 100, 0);
} else {
cout << "max 64 clients for now." << endl;
}
} else {
// 接收客户端的数据
char buf[100] = {0};
ret = recv(selectedSocket, buf, 100, 0);
if (ret == SOCKET_ERROR || ret == 0) {
closesocket(selectedSocket);
FD_CLR(selectedSocket, &readSet);
cout << selectedSocket << "logged off." << endl;
} else {
cout << selectedSocket << " sent " << buf << endl;
}
}
}
}
// 关闭监听套接字
closesocket(sListen);
// 清理winsock环境
WSACleanup();
return 0;
}
客户端启动,连接127.0.0.1:8000
#include<winsock2.h>//winsock2的头文件
#include<iostream>
using namespace std;
//勿忘,链接dll的lib
#pragma comment(lib, "ws2_32.lib")
int main() {
//加载winsock2的环境
WSADATA wd;
if (WSAStartup(MAKEWORD(2, 2), &wd) != 0) {
cout << "WSAStartup error:" << GetLastError() << endl;
return 0;
}
//1.创建流式套接字
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET) {
cout << "socket error:" << GetLastError() << endl;
return 0;
}
//2.连接服务器
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int len = sizeof(sockaddr_in);
if (connect(s, (SOCKADDR *) &addr, len) == SOCKET_ERROR) {
cout << "connect error:" << GetLastError() << endl;
return 0;
}
//3接收服务端的消息
char buf[100] = {0};
recv(s, buf, 100, 0);
cout << buf << endl;
//3随时给服务端发消息
int ret = 0;
do {
char buf[100] = {0};
cout << "Please input something:";
cin >> buf;
ret = send(s, buf, 100, 0);
} while (ret != SOCKET_ERROR && ret != 0);
//4.关闭监听套接字
closesocket(s);
//清理winsock2的环境
WSACleanup();
return 0;
}