select

select维护了一个监测队列,当我们把需要的文件描述符传入时,它就会主动监测其中的内容。

一般格式为

1
2
3
4
5
6
7
8
9
10
fd_set fdRead;//监测队列
FD_ZERO(&fdRead);
FD_SET(servSock, &fdRead);//sock加入监测队列
for (SOCKET client : clientList)
{
FD_SET(client, &fdRead);
}


int selectRes = select(0, &fdRead, nullptr, nullptr, &tv);//selectRes表明select是否监测到sock的活动

由于服务器socket活动时表明有新连接
客户端socket活动时表明有数据发送,
所以通过这个区别来添加新客户端和发送信息。

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
bool Server::coreFunc() {
assert(servSock != INVALID_SOCKET);
while (1) { // 无限循环,持续处理事件
// 1. 初始化 fd_set,添加服务端套接字和所有客户端套接字
fd_set fdRead;
FD_ZERO(&fdRead);
FD_SET(servSock, &fdRead); // 监控服务端套接字(新连接)
for (SOCKET client : clientList) {
FD_SET(client, &fdRead); // 监控所有客户端套接字(数据到达)
}

// 2. 调用 select,设置超时时间为 1 秒
timeval tv{ 1, 0 };
int selectRes = select(0, &fdRead, nullptr, nullptr, &tv);

// 3. 处理 select 结果
if (selectRes > 0) { // 有套接字活动
for (size_t i = 0; i < fdRead.fd_count; ++i) { // 遍历活跃的套接字
SOCKET activeSock = fdRead.fd_array[i];
if (activeSock == servSock) { // 服务端套接字活跃:新连接
SOCKET clntSock = accept(servSock, nullptr, nullptr);
if (clntSock == INVALID_SOCKET) {
std::cout << "accept error" << std::endl;
break;
}
clientList.push_back(clntSock); // 将新客户端加入列表
} else { // 客户端套接字活跃:数据到达
recvMsg(fdRead, activeSock); // 接收并处理数据
}
}
} else if (selectRes == 0) { // 超时,无活动
std::cout << "do something else" << std::endl;
continue;
} else { // select 错误
std::cout << "select func error" << WSAGetLastError() << std::endl;
return false;
}
}
return true;
}

因此服务端完整代码

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
//Server.hpp
#pragma once

#include <WinSock2.h>
#include <WS2tcpip.h>
#include "Message.hpp"
#include <list>
#include <assert.h>
#pragma comment(lib,"ws2_32.lib")

class Server
{
private:
SOCKET servSock;
std::list<SOCKET> clientList;
bool coreFunc();
public:
Server() :servSock(INVALID_SOCKET) {};
~Server();
void close();
bool init(const char* ip, unsigned short port);
bool start();
bool recvMsg(fd_set& fdRead, SOCKET& clientSock);
//bool conductMsg();
};


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
//Server.cpp
#include "server.h"

#include <iostream>
#include <ostream>

Server::~Server()
{
close();
}
void Server::close()
{
if (servSock != INVALID_SOCKET)
{
closesocket(servSock);
for (SOCKET client : clientList)
closesocket(client);
WSACleanup();
}
}
bool Server::init(const char* ip, unsigned short port)
{
WSADATA data{};
if (WSAStartup(MAKEWORD(2, 2), &data) == SOCKET_ERROR)
{
std::cout << "startup error" << std::endl;
return false;
}
servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (servSock == INVALID_SOCKET)
{
std::cout << "socket error" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
sockaddr_in servAddr{};

servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(2345);
inet_pton(AF_INET, "127.0.0.1", &servAddr.sin_addr.S_un.S_addr);

if (bind(servSock, (sockaddr*)&servAddr, sizeof(sockaddr)) == SOCKET_ERROR)
{
std::cout << "bind error" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}

if (listen(servSock, 128) == SOCKET_ERROR)
{
std::cout << "listen error" << std::endl;
WSACleanup();
return false;
}
return true;
}
bool Server::start()
{
if (!coreFunc())
{
return false;
}
}
bool Server::coreFunc()
{
assert(servSock != INVALID_SOCKET);
while (1)
{
fd_set fdRead;
FD_ZERO(&fdRead);
FD_SET(servSock, &fdRead);
for (SOCKET client : clientList)
{
FD_SET(client, &fdRead);
}
timeval tv{ 1,0 };
int selectRes = select(0, &fdRead, nullptr, nullptr, &tv);
if (selectRes > 0)
{
for (size_t i = 0; i < fdRead.fd_count; ++i)
{
if (fdRead.fd_array[i] == servSock)
{
SOCKET clntSock = accept(servSock, nullptr, nullptr);
if (clntSock == INVALID_SOCKET)
{
std::cout << "accept error" << std::endl;
break;
}
clientList.push_back(clntSock);
}
else
{
recvMsg(fdRead, fdRead.fd_array[i]);
}
}
}
else if (selectRes == 0)
{
std::cout << "do something else" << std::endl;
continue;
}
else
{
std::cout << "select func error" << WSAGetLastError() << std::endl;
return false;
}
}
return true;
}
bool Server::recvMsg(fd_set& fdRead, SOCKET& clientSock)
{
Header header;
int recvRes = recv(clientSock, (char*)&header, sizeof(Header), 0);
if (recvRes <= 0)
{
std::cout << "Client disconnected:" << clientSock << std::endl;
clientList.erase(std::remove(clientList.begin(), clientList.end(), clientSock), clientList.end());
closesocket(clientSock);

return false;
}
switch (header.cmd)
{
case CMD::LOGIN:
{
Login login;
int recvRes2 = recv(clientSock, (char*)&login + sizeof(Header),
sizeof(Login) - sizeof(Header), 0);
if (recvRes2 <= 0)
{
std::cout << "recv error" << std::endl;
return false;
}
LoginResult loginResult(10);
if (send(clientSock, (char*)&loginResult, sizeof(LoginResult), 0) < 0)
{
std::cout << "send error" << std::endl;
return false;
}
break;
}
case CMD::LOGOUT:
{
Logout logout;
int recvRes2 = recv(clientSock, (char*)&logout + sizeof(Header),
sizeof(Logout) - sizeof(Header), 0);
if (recvRes2 <= 0)
{
std::cout << "recv error" << std::endl;
return false;
}
LogoutResult logoutResult(10);
if (send(clientSock, (char*)&logoutResult, sizeof(LoginResult), 0) < 0)
{
std::cout << "send error" << std::endl;
return false;
}
break;
}
case CMD::LOGIN_RESULT:
break;
case CMD::LOGOUT_RESULT:
break;
}
}
//bool Server::conductMsg() {}

1
2
3
4
5
6
7
8
9
10
//main
int main(int argc, char* argv[])
{
Server server;
if (!server.init("127.0.0.1", 2345))
return 1;
server.start();
system("pause");
}

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
//message.hpp
#pragma once
#include <string.h>

enum class CMD
{
LOGIN,
LOGOUT,
LOGIN_RESULT,
LOGOUT_RESULT,
NO_CMD
};


struct Header
{
CMD cmd;
unsigned dataLength;

Header() : cmd(CMD::NO_CMD)
, dataLength(0) {
}

Header(CMD cmd_, unsigned dataLength_) : cmd(cmd_)
, dataLength(dataLength_) {
}
};

struct Login :public Header
{
char usrName[32];
char passwd[32];
Login(const char* userName_, const char* passwd_) :Header(CMD::LOGIN, sizeof(Login))
{
strcpy_s(usrName, 32, userName_);
strcpy_s(passwd, 32, passwd_);

}
Login() :Header(CMD::LOGIN, sizeof(Login)), usrName{ 0 }, passwd{ 0 } {}
};


struct Logout : public Header
{
unsigned logoutNum;

Logout(unsigned logoutNum_) : Header(CMD::LOGOUT, sizeof(Logout))
, logoutNum(logoutNum_) {
}
Logout() :Header(CMD::LOGOUT, sizeof(Logout)), logoutNum(0) {}

};


struct LoginResult :public Header
{
unsigned loginResultNum;
LoginResult(unsigned loginResultNum_) :Header(CMD::LOGIN_RESULT, sizeof(LoginResult)), loginResultNum(loginResultNum_) {}
LoginResult() :Header(CMD::LOGIN_RESULT, sizeof(LoginResult)), loginResultNum(0) {}
};

struct LogoutResult :public Header
{
unsigned logoutResultNum;
LogoutResult(unsigned logoutResultNum_) :Header(CMD::LOGOUT_RESULT, sizeof(LogoutResult)), logoutResultNum(logoutResultNum_) {}
LogoutResult() :Header(CMD::LOGOUT_RESULT, sizeof(LogoutResult)), logoutResultNum(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
#include <iostream>
#include <list>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include "Message.hpp"
#pragma comment(lib,"ws2_32.lib")

int main(int argc, char* argv[])
{
WSADATA data{};

if (WSAStartup(MAKEWORD(2, 2), &data) == SOCKET_ERROR)
{
std::cout << "WSAstartup error" << std::endl;
return 1;
}
SOCKET servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (servSock == INVALID_SOCKET)
{
std::cout << "socket error" << std::endl;
WSACleanup();
return 1;
}
sockaddr_in servAddr{};
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(2345);
inet_pton(AF_INET, "127.0.0.1", &servAddr.sin_addr.S_un.S_addr);
if (connect(servSock, (sockaddr*)&servAddr, sizeof(sockaddr)) == SOCKET_ERROR)
{
std::cout << "connect error" << std::endl;
closesocket(servSock);
WSACleanup();
return 1;
}

std::cout << "connect success" << std::endl;

while (true)
{
Login login("aaaa", "bbbb");
if (send(servSock, reinterpret_cast<char*>(&login), sizeof(Login), 0) < 0)
{
std::cout << "send error" << std::endl;
break;
}
LoginResult loginResult;
if (recv(servSock, reinterpret_cast<char*>(&loginResult), sizeof(LoginResult), 0) <= 0)
{
std::cout << "recv error" << std::endl;
break;
}
std::cout << loginResult.loginResultNum << std::endl;
Sleep(100);
}


system("pause");



}