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
252
253
254
|
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <WinSock2.h>
#include <cstdio>
#include <vector>
#pragma comment(lib,"ws2_32.lib")
enum CMD //命令枚举
{
CMD_LOGIN,
CMD_LOGIN_RESULT,
CMD_LOGOUT,
CMD_LOGOUT_RESULT,
CMD_NEW_USER_JOIN,
CMD_ERROR
};
//DataHeader
struct DataHeader //数据包头
{
short dataLength;
short cmd;
};
//DataPackage
struct Login:public DataHeader //登录
{
Login()
{
dataLength = sizeof(Login);
cmd = CMD_LOGIN;
}
char UserName[32]{};
char PassWord[32]{};
};
struct LoginResult : public DataHeader //登录结果
{
LoginResult()
{
dataLength = sizeof(LoginResult);
cmd = CMD_LOGIN_RESULT;
lgResult = 0;
}
int lgResult;
};
struct LogOut :public DataHeader //退出登录
{
LogOut()
{
dataLength = sizeof(LogOut);
cmd = CMD_LOGOUT;
}
char UserName[32]{};
};
struct LogOutResult :public DataHeader //退出结果
{
LogOutResult()
{
dataLength = sizeof(LogOutResult);
cmd = CMD_LOGOUT_RESULT;
lgOutResult = 0;
}
int lgOutResult;
};
struct NewUserJoin :public DataHeader //新加入用户
{
NewUserJoin()
{
dataLength = sizeof(NewUserJoin);
cmd = CMD_NEW_USER_JOIN;
sockID = 0;
}
int sockID;
};
std::vector<SOCKET> g_clients;
int Processor(SOCKET sockAccpt)
{//缓冲区
char szRecv[1024] = {};
//读取包头数据
int nLen = recv(sockAccpt, (char*)&szRecv, sizeof(DataHeader), 0);
DataHeader* dbHeader = (DataHeader*)szRecv;
if (nLen < 0)
{
printf("客户端<%d>已退出,任务结束\n", sockAccpt);
return -1;
}
//if(nLen >= sizeof(DataHeader))
switch (dbHeader->cmd)
{
case CMD_LOGIN:
{
recv(sockAccpt, szRecv + sizeof(DataHeader), dbHeader->dataLength - sizeof(DataHeader), 0);
Login* login = (Login*)szRecv;
printf("收到客户端<Socket%d>请求:CMD_LOGIN ,数据长度: %d, UserName = %s, \
PassWord = %s \n", sockAccpt, login->dataLength, login->UserName, login->PassWord);
//忽略对用户密码进行判断
LoginResult lgRet = {};
send(sockAccpt, (char*)&lgRet, sizeof(LoginResult), 0);
}
break;
case CMD_LOGOUT:
{
recv(sockAccpt, szRecv + sizeof(DataHeader), dbHeader->dataLength - sizeof(DataHeader), 0);
LogOut* logout = (LogOut*)szRecv;
printf("收到客户端<Socket%d>请求:CMD_LOGOUT ,数据长度: %d, UserName = %s, \
\n", sockAccpt, logout->dataLength, logout->UserName);
//忽略对用户密码进行判断
LogOutResult lgOutRet = {};
send(sockAccpt, (char*)&lgOutRet, sizeof(LogOutResult), 0);
}
break;
default:
DataHeader HeaderError = { 0, CMD_ERROR };
send(sockAccpt, (char*)&HeaderError, sizeof(HeaderError), 0);
break;
}
}
int main()
{
WORD ver = MAKEWORD(2, 2);
WSAData dat;
WSAStartup(ver, &dat);
//1.创建socket套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//2,bind 绑定用于接收客户端连接的端口
sockaddr_in sinAddr = {};
sinAddr.sin_family = AF_INET;
sinAddr.sin_port = htons(5678); //host to net unsigned short
sinAddr.sin_addr.S_un.S_addr = INADDR_ANY; //inet_addr("127.0.0.1")
if (SOCKET_ERROR == bind(sock, (sockaddr*)&sinAddr, sizeof(sockaddr_in)))
{
printf("Bind Error\n");
}
else
{
printf("Bind Success\n");
}
//3. listen 监听网络端口
if (SOCKET_ERROR == listen(sock, 5))
{
printf("Listen Error\n");
}
else
{
printf("Listen Success\n");
}
while (true)
{
//伯克利 socket
fd_set fdRead;
fd_set fdWrite;
fd_set fdExp;
//清空集合
FD_ZERO(&fdRead);
FD_ZERO(&fdWrite);
FD_ZERO(&fdExp);
//将socket加入集合
FD_SET(sock, &fdRead);
FD_SET(sock, &fdWrite);
FD_SET(sock, &fdExp);
//将存的通信符加入集合
for (size_t i = 0; i < g_clients.size(); i++)
{
FD_SET(g_clients[i], &fdRead);
}
//将select设置为非阻塞模式
timeval timeout = { 0,0 };
//使用select
int ret = select(sock + 1, &fdRead, &fdWrite, &fdExp, &timeout);
if (ret < 0)
{
printf("select任务结束\n");
break;
}
if (FD_ISSET(sock, &fdRead))
{
FD_CLR(sock, &fdRead);
//4.accept 接收客户端连接
sockaddr_in clientAddr = {};
int clAddrLen = sizeof(sockaddr_in);
SOCKET sockAccpt = INVALID_SOCKET;
sockAccpt = accept(sock, (sockaddr*)&clientAddr, &clAddrLen);
if (INVALID_SOCKET == sockAccpt)
{
printf("Accept Error\n");
}
else
{
//群发消息
for (size_t i = 0; i < g_clients.size(); i++)
{
NewUserJoin userJoin;
send(g_clients[i], (const char*)&userJoin, sizeof(NewUserJoin), 0);
}
//将新加入的通信文件描述符加入
g_clients.push_back(sockAccpt);
printf("新客户端加入:Socket = %d,IP = %s \n", (int)sockAccpt, inet_ntoa(clientAddr.sin_addr));
}
}
//处理集合中的文件描述符对应的通信信息
for (size_t i = 0; i <fdRead.fd_count ; i++)
{
int ret = Processor(fdRead.fd_array[i]);
if (-1 == ret)
{
//出现错误,从动态数组中删除
auto iter = find(g_clients.begin(), g_clients.end(), fdRead.fd_array[i]);
if (iter != g_clients.end())
{
g_clients.erase(iter);
}
}
}
}
//关闭通信文件描述符
for (size_t i = 0; i < g_clients.size(); i++)
{
closesocket(g_clients[i]);
}
//closesocket 关闭套接字
closesocket(sock);
WSACleanup();
printf("结束任务\n");
getchar();
return 0;
}
|