执行阻塞:
默认情况下socket是blocking的,即函数accept(), recv/recvfrom, send/sendto,connect等,需等待函数执行结束之后才能够返回
(此时操作系统切换到其他进程执行)。accpet()等待到有client连接请求并接受成功之后,recv/recvfrom需要读取完client发送的数据之后才能够返回。
阻塞与非阻塞套接字
sockets_tutorial
这几种网络模型就是解决阻塞问题的,简单的模型解决傻等阻塞,复杂的模型解决执行阻塞。
本文思维导图
模型用于服务器
解决服务器无法有多个客户端连接的情况。
代码逻辑:
select处理逻辑
各种 Windows 套接字函数和服务提供程序(如 select 函数)使用fd_set结构将套接字放入"set"中,用于各种目的,例如使用 select 函数的 readfds 参数测试给定套接字的可读性。
本质就是一个数组
注意
fd_set fd_set_socks;//装服务端、多个客户端的集合
FD_ZERO(&fd_set_socks);//只将count清0 -> 高效
FD_SET(socket_server, &fd_set_socks);//添加一个元素
int res_set = FD_ISSET(socket_server, &fd_set_socks);//判断一个socket是否在集合中
if (0 == res_set)
printf("不在集合中, res_set = %d\n", res_set);
else
printf("在集合中, res_set = %d\n", res_set);
FD_CLR(socket_server, &fd_set_socks);//删除一个元素
res_set = FD_ISSET(socket_server, &fd_set_socks);//判断一个socket是否在集合中
if (0 == res_set)
printf("不在集合中, res_set = %d\n", res_set);
else
printf("在集合中, res_set = %d\n", res_set);
closesocket(socket_server);//从集合中删除后要手动释放socket,否则会造成内存泄漏
select 函数确定一个或多个套接字的状态,如有必要,请等待执行同步 I/O。
语法
int WSAAPI select(
[in] int nfds,
[in, out] fd_set *readfds,
[in, out] fd_set *writefds,
[in, out] fd_set *exceptfds,
[in] const timeval *timeout
);
忽视。包含 nfds 参数只是为了与 Berkeley 套接字兼容。
指向一组要检查可读性的套接字的可选指针。
in -> 最初的fd_set结构体 如:1,2,3,4,5
out -> 返回有响应的客户端,如1,2响应,就readfds 就是1 和 2
fd_set fd_set_socks;//装服务端和多个客户端的集合 1,2,3,4,5
select(0, &fd_set_socks, &fd_set_socks, &fd_set_socks, &time_val);//此时,fd_set_socks为1,2
检查 处理 accept 和 recv
测试参数2的demo
select_server.c
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#pragma comment(lib,"Ws2_32.lib")
int main(int argc, char* argv[])
{
/*打开网络库*/
WORD ws_verson = MAKEWORD(2, 2);
WSADATA wd_sock_msg;
int ret = WSAStartup(ws_verson, &wd_sock_msg);
if (0 != ret)
{
switch (ret)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库!");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库!");
break;
case WSAEPROCLIM:
printf("请重新启动!");
break;
case WSAEINPROGRESS:
printf("请尝试关掉不必要的软件,为当前网络运行提供充足资源!");
break;
case WSAEFAULT:
printf("参数错误!");
break;
default:
break;
}
return 0;
}
printf("网络库打开成功!\n");
/*检查版本*/
if (2 != LOBYTE(wd_sock_msg.wVersion) || //地位-》得到主版本
2 != HIBYTE(wd_sock_msg.wVersion)) //高位-》得到副版本
{
//说明版本不对,要关闭网络库
WSACleanup();
return -1;
}
printf("网络库版本号:(%d,%d)\n", LOBYTE(wd_sock_msg.wVersion), HIBYTE(wd_sock_msg.wVersion));
/*创建套接字*/
SOCKET socket_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket_server)
{
printf("socket created failed,error_code:%d\n", WSAGetLastError());
WSACleanup();//关闭网络库
return -1;
}
else
printf("socket created success,return code:%d\n", socket_server);
/*绑定地址*/
struct sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = htons(12345);
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int res_bind = bind(socket_server, (const struct sockaddr *) &si, sizeof(si));
if (SOCKET_ERROR == res_bind)
{
printf("bind failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("bind success,return code:%d\n", res_bind);
/*监听listen*/
int res_listen = listen(socket_server, SOMAXCONN);
if (SOCKET_ERROR == res_listen)
{
printf("listen failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("listen success,return code:%d\n", res_listen);
/*select模型*/
/*第1步*/
fd_set fd_set_socks;//装服务端和多个客户端的集合
FD_ZERO(&fd_set_socks);//只将count清0 -> 高效
FD_SET(socket_server, &fd_set_socks);//将服务端装进去
while (1)
{
fd_set fd_set_socks_temp = fd_set_socks;
struct timeval time_val;
time_val.tv_sec = 3;
time_val.tv_usec = 0;
//第1种情况,检查accept 和 recv
int res_select = select(0, &fd_set_socks_temp, NULL, NULL, &time_val);
if (0 == res_select)//无响应
{
continue;
}
else if (res_select > 0)//有响应
{
/* 判断响应是服务端产生的还是客户端产生的
响应的是服务端,说明有客户端连接进来 -> accept
响应的是客户端,说明有客户端 下线/向服务端发送消息/错误 -> 具体处理
*/
for (u_int i = 0; i < fd_set_socks_temp.fd_count; i++)
{
if (fd_set_socks_temp.fd_array[i] == socket_server)//是服务端响应
{
//accept
SOCKET socket_client = accept(socket_server, NULL, NULL);
if (INVALID_SOCKET == socket_client)//连接出错
continue;
FD_SET(socket_client, &fd_set_socks);//链接客户端成功,放进fd_set_socks中
//send
//...
}
else//是客户端响应
{
char str_buf[1500] = { 0 };
int res_recv = recv(fd_set_socks_temp.fd_array[i], str_buf, 1499, 0);
if (0 == res_recv)//客户端下线了
{
printf("socket_id: %d 下线\n", fd_set_socks.fd_array[i]);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
else if (res_recv > 0)//接收到了消息
{
printf("服务器socket%d接收到了客户端的消息:%s\n", fd_set_socks.fd_array[i], str_buf);
}
else//SOCK_ERROR
{
int err_code = WSAGetLastError();
//客户端强行关闭 出错 error_code 10054
switch (err_code)
{
case 10054:
{
{
printf("client offline,error_code:%d\n", err_code);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
}
break;
}
}
}
}
}
else//发生错误了
{
break;
}
}
//int res_set = FD_ISSET(socket_server, &fd_set_socks);//判断一个socket是否在集合中
//if (0 == res_set)
// printf("不在集合中, res_set = %d\n", res_set);
//else
// printf("在集合中, res_set = %d\n", res_set);
//FD_CLR(socket_server, &fd_set_socks);//删除一个元素
//res_set = FD_ISSET(socket_server, &fd_set_socks);//判断一个socket是否在集合中
//if (0 == res_set)
// printf("不在集合中, res_set = %d\n", res_set);
//else
// printf("在集合中, res_set = %d\n", res_set);
/*释放所有的socket*/
/*程序结束先关闭socket,再关闭网络库;因为socket是网络库里面的函数*/
for (u_int i = 0; i < fd_set_socks.fd_count; i++)
{
closesocket(fd_set_socks.fd_array[i]);//从集合中删除后要手动释放socket,否则会造成内存泄漏
}
/*关闭网络库*/
WSACleanup();//关闭网络库
printf("\n");
system("pause");
return 0;
}
client.c
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#pragma comment(lib, "Ws2_32.lib")
int main(void)
{
WORD wdVersion = MAKEWORD(2, 2);
WSADATA wdScokMsg;
int nRes = WSAStartup(wdVersion, &wdScokMsg);
if (0 != nRes)
{
switch (nRes)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库");
break;
case WSAEINPROGRESS:
printf("请重新启动");
break;
case WSAEPROCLIM:
printf("请尝试关掉不必要的软件,以为当前网络运行提供充足资源");
break;
}
return 0;
}
//校验版本
if (2 != HIBYTE(wdScokMsg.wVersion) || 2 != LOBYTE(wdScokMsg.wVersion))
{
//说明版本不对
//清理网络库
WSACleanup();
return 0;
}
//服务器socket
SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socketServer)
{
int a = WSAGetLastError();
//清理网络库
WSACleanup();
return 0;
}
//链接服务器
struct sockaddr_in serverMsg;
serverMsg.sin_family = AF_INET;
serverMsg.sin_port = htons(12345);
serverMsg.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (SOCKET_ERROR == connect(socketServer, (struct sockaddr*)&serverMsg, sizeof(serverMsg)))
{
int a = WSAGetLastError();
closesocket(socketServer);
//清理网络库
WSACleanup();
return 0;
}
while (1)
{
char buf[1500] = { 0 };
scanf("%s", buf);
if ('q' == buf[0])//当输入q的时候退出循环,关闭程序,正常下线
{
break;
}
if (SOCKET_ERROR == send(socketServer, buf, strlen(buf), 0))
{
//出错了
int a = WSAGetLastError();
//根据实际情况处理
printf("%d\n", a);
}
}
/*释放所有的socket*/
/*程序结束先关闭socket,再关闭网络库;因为socket是网络库里面的函数*/
for (u_int i = 0; i < fd_set_socks.fd_count; i++)
{
closesocket(fd_set_socks.fd_array[i]);//从集合中删除后要手动释放socket,否则会造成内存泄漏
}
/*关闭网络库*/
WSACleanup();//关闭网络库
system("pause");
return 0;
}
指向要检查可写性的一组套接字的可选指针。
检查 处理 send
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#pragma comment(lib,"Ws2_32.lib")
int main(int argc, char* argv[])
{
/*打开网络库*/
WORD ws_verson = MAKEWORD(2, 2);
WSADATA wd_sock_msg;
int ret = WSAStartup(ws_verson, &wd_sock_msg);
if (0 != ret)
{
switch (ret)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库!");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库!");
break;
case WSAEPROCLIM:
printf("请重新启动!");
break;
case WSAEINPROGRESS:
printf("请尝试关掉不必要的软件,为当前网络运行提供充足资源!");
break;
case WSAEFAULT:
printf("参数错误!");
break;
default:
break;
}
return 0;
}
printf("网络库打开成功!\n");
/*检查版本*/
if (2 != LOBYTE(wd_sock_msg.wVersion) || //地位-》得到主版本
2 != HIBYTE(wd_sock_msg.wVersion)) //高位-》得到副版本
{
//说明版本不对,要关闭网络库
WSACleanup();
return -1;
}
printf("网络库版本号:(%d,%d)\n", LOBYTE(wd_sock_msg.wVersion), HIBYTE(wd_sock_msg.wVersion));
/*创建套接字*/
SOCKET socket_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket_server)
{
printf("socket created failed,error_code:%d\n", WSAGetLastError());
WSACleanup();//关闭网络库
return -1;
}
else
printf("socket created success,return code:%d\n", socket_server);
/*绑定地址*/
struct sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = htons(12345);
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int res_bind = bind(socket_server, (const struct sockaddr *) &si, sizeof(si));
if (SOCKET_ERROR == res_bind)
{
printf("bind failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("bind success,return code:%d\n", res_bind);
/*监听listen*/
int res_listen = listen(socket_server, SOMAXCONN);
if (SOCKET_ERROR == res_listen)
{
printf("listen failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("listen success,return code:%d\n", res_listen);
/*select模型*/
/*第1步*/
fd_set fd_set_socks;//装服务端和多个客户端的集合
FD_ZERO(&fd_set_socks);//只将count清0 -> 高效
FD_SET(socket_server, &fd_set_socks);//将服务端装进去
while (1)
{
fd_set fd_set_read_socks = fd_set_socks;
fd_set fd_set_write_socks = fd_set_socks;//里面包含了server
FD_CLR(socket_server, &fd_set_write_socks);//先把服务器删掉
struct timeval time_val;
time_val.tv_sec = 3;
time_val.tv_usec = 0;
//第1种情况,检查accept 和 recv
int res_select = select(0, &fd_set_read_socks, &fd_set_write_socks, NULL, &time_val);
if (0 == res_select)//无响应
{
continue;
}
else if (res_select > 0)//有响应
{
/*判断客户端是否可写*/
for (u_int i = 0; i < fd_set_write_socks.fd_count; i++)
{
//printf("服务器:%d, socket id %d 可写\n", socket_server, fd_set_write_socks.fd_array[i]);
//send
int res_send = send(fd_set_write_socks.fd_array[i], "ok", 2, 0);
if (SOCKET_ERROR == res_send)
{
printf("send error error_code:%d\n", WSAGetLastError());
}
else if (2 == res_send)
{
//printf("发送成功");
}
}
/* 判断响应是服务端产生的还是客户端产生的
响应的是服务端,说明有客户端连接进来 -> accept
响应的是客户端,说明有客户端 下线/向服务端发送消息/错误 -> 具体处理
*/
for (u_int i = 0; i < fd_set_read_socks.fd_count; i++)
{
if (fd_set_read_socks.fd_array[i] == socket_server)//是服务端响应
{
//accept
SOCKET socket_client = accept(socket_server, NULL, NULL);
if (INVALID_SOCKET == socket_client)//连接出错
continue;
FD_SET(socket_client, &fd_set_socks);//链接客户端成功,放进fd_set_socks中
//send
//...
}
else//是客户端响应
{
char str_buf[1500] = { 0 };
int res_recv = recv(fd_set_read_socks.fd_array[i], str_buf, 1499, 0);
if (0 == res_recv)//客户端下线了
{
printf("socket_id: %d 下线\n", fd_set_socks.fd_array[i]);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
else if (res_recv > 0)//接收到了消息
{
printf("服务器socket%d %d接收到了客户端的消息:%s\n",
socket_server, fd_set_socks.fd_array[i], str_buf);
}
else//SOCK_ERROR
{
int err_code = WSAGetLastError();
//客户端强行关闭 出错 error_code 10054
switch (err_code)
{
case 10054:
{
{
printf("client offline,error_code:%d\n", err_code);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
}
break;
}
}
}
}
}
else//发生错误了
{
break;
}
}
/*释放所有的socket*/
/*程序结束先关闭socket,再关闭网络库;因为socket是网络库里面的函数*/
for (u_int i = 0; i < fd_set_socks.fd_count; i++)
{
closesocket(fd_set_socks.fd_array[i]);//从集合中删除后要手动释放socket,否则会造成内存泄漏
}
/*关闭网络库*/
WSACleanup();//关闭网络库
printf("\n");
system("pause");
return 0;
}
指向一组要检查错误的套接字的可选指针。
检查 处理 error
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#pragma comment(lib,"Ws2_32.lib")
int main(int argc, char* argv[])
{
/*打开网络库*/
WORD ws_verson = MAKEWORD(2, 2);
WSADATA wd_sock_msg;
int ret = WSAStartup(ws_verson, &wd_sock_msg);
if (0 != ret)
{
switch (ret)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库!");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库!");
break;
case WSAEPROCLIM:
printf("请重新启动!");
break;
case WSAEINPROGRESS:
printf("请尝试关掉不必要的软件,为当前网络运行提供充足资源!");
break;
case WSAEFAULT:
printf("参数错误!");
break;
default:
break;
}
return 0;
}
printf("网络库打开成功!\n");
/*检查版本*/
if (2 != LOBYTE(wd_sock_msg.wVersion) || //地位-》得到主版本
2 != HIBYTE(wd_sock_msg.wVersion)) //高位-》得到副版本
{
//说明版本不对,要关闭网络库
WSACleanup();
return -1;
}
printf("网络库版本号:(%d,%d)\n", LOBYTE(wd_sock_msg.wVersion), HIBYTE(wd_sock_msg.wVersion));
/*创建套接字*/
SOCKET socket_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket_server)
{
printf("socket created failed,error_code:%d\n", WSAGetLastError());
WSACleanup();//关闭网络库
return -1;
}
else
printf("socket created success,return code:%d\n", socket_server);
/*绑定地址*/
struct sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = htons(12345);
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int res_bind = bind(socket_server, (const struct sockaddr *) &si, sizeof(si));
if (SOCKET_ERROR == res_bind)
{
printf("bind failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("bind success,return code:%d\n", res_bind);
/*监听listen*/
int res_listen = listen(socket_server, SOMAXCONN);
if (SOCKET_ERROR == res_listen)
{
printf("listen failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("listen success,return code:%d\n", res_listen);
/*select模型*/
/*第1步*/
fd_set fd_set_socks;//装服务端和多个客户端的集合
FD_ZERO(&fd_set_socks);//只将count清0 -> 高效
FD_SET(socket_server, &fd_set_socks);//将服务端装进去
while (1)
{
fd_set fd_set_read_socks = fd_set_socks;
fd_set fd_set_write_socks = fd_set_socks;//里面包含了server
FD_CLR(socket_server, &fd_set_write_socks);//先把服务器删掉
fd_set fd_set_err_socks = fd_set_socks;
struct timeval time_val;
time_val.tv_sec = 3;
time_val.tv_usec = 0;
//第1种情况,检查accept 和 recv
int res_select = select(0, &fd_set_read_socks, &fd_set_write_socks, &fd_set_err_socks, &time_val);
if (0 == res_select)//无响应
{
continue;
}
else if (res_select > 0)//有响应
{
/*处理错误*/
for (u_int i = 0; i < fd_set_err_socks.fd_count; i++)
{
char err_buf[100] = { 0 };
int len = 99;
int res_err = getsockopt(fd_set_err_socks.fd_array[i], SOL_SOCKET, SO_ERROR, err_buf, &len);
if (SOCKET_ERROR == res_err)
{
printf("getsockopt failed, err_code %d\n", WSAGetLastError());
}
else if (0 == res_err)
{
printf("no error happens\n");
}
}
/*判断客户端是否可写*/
for (u_int i = 0; i < fd_set_write_socks.fd_count; i++)
{
//printf("服务器:%d, socket id %d 可写\n", socket_server, fd_set_write_socks.fd_array[i]);
//send
int res_send = send(fd_set_write_socks.fd_array[i], "ok", 2, 0);
if (SOCKET_ERROR == res_send)
{
printf("send error error_code:%d\n", WSAGetLastError());
}
else if (2 == res_send)
{
//printf("发送成功");
}
}
/* 判断响应是服务端产生的还是客户端产生的
响应的是服务端,说明有客户端连接进来 -> accept
响应的是客户端,说明有客户端 下线/向服务端发送消息/错误 -> 具体处理
*/
for (u_int i = 0; i < fd_set_read_socks.fd_count; i++)
{
if (fd_set_read_socks.fd_array[i] == socket_server)//是服务端响应
{
//accept
SOCKET socket_client = accept(socket_server, NULL, NULL);
if (INVALID_SOCKET == socket_client)//连接出错
continue;
FD_SET(socket_client, &fd_set_socks);//链接客户端成功,放进fd_set_socks中
//send
//...
}
else//是客户端响应
{
char str_buf[1500] = { 0 };
int res_recv = recv(fd_set_read_socks.fd_array[i], str_buf, 1499, 0);
if (0 == res_recv)//客户端下线了
{
printf("socket_id: %d 下线\n", fd_set_socks.fd_array[i]);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
else if (res_recv > 0)//接收到了消息
{
printf("服务器socket%d %d接收到了客户端的消息:%s\n",
socket_server, fd_set_socks.fd_array[i], str_buf);
}
else//SOCK_ERROR
{
int err_code = WSAGetLastError();
//客户端强行关闭 出错 error_code 10054
switch (err_code)
{
case 10054:
{
{
printf("client offline,error_code:%d\n", err_code);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
}
break;
}
}
}
}
}
else//发生错误了
{
break;
}
}
/*释放所有的socket*/
/*程序结束先关闭socket,再关闭网络库;因为socket是网络库里面的函数*/
for (u_int i = 0; i < fd_set_socks.fd_count; i++)
{
closesocket(fd_set_socks.fd_array[i]);//从集合中删除后要手动释放socket,否则会造成内存泄漏
}
/*关闭网络库*/
WSACleanup();//关闭网络库
printf("\n");
system("pause");
return 0;
}
选择等待的最长时间
,以 TIMEVAL 结构的形式提供。对于阻止操作,将超时参数设置为 null。
struct timeval time_val;
time_val.tv_sec = 3;
time_val.tv_usec = 0;
//select()函数会处理3秒的时间,执行阻塞
int res_select = select(0, &fd_set_read_socks, &fd_set_write_socks, &fd_set_err_socks, &time_val);
fd_set fd_set_socks;//装服务端和多个客户端的集合
BOOL WINAPI fun(DWORD dw_ctrl_type)
{
switch (dw_ctrl_type)
{
case CTRL_CLOSE_EVENT:
{
{
/*释放所有的socket*/
/*程序结束先关闭socket,再关闭网络库;因为socket是网络库里面的函数*/
for (u_int i = 0; i < fd_set_socks.fd_count; i++)
{
closesocket(fd_set_socks.fd_array[i]);//从集合中删除后要手动释放socket,否则会造成内存泄漏
}
/*关闭网络库*/
WSACleanup();//关闭网络库
}
}
break;
default:
break;
}
return TRUE;
}
SetConsoleCtrlHandler(fun, TRUE);
selece_server.c
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#pragma comment(lib,"Ws2_32.lib")
fd_set fd_set_socks;//装服务端和多个客户端的集合
BOOL WINAPI fun(DWORD dw_ctrl_type)
{
switch (dw_ctrl_type)
{
case CTRL_CLOSE_EVENT:
{
{
/*释放所有的socket*/
/*程序结束先关闭socket,再关闭网络库;因为socket是网络库里面的函数*/
for (u_int i = 0; i < fd_set_socks.fd_count; i++)
{
closesocket(fd_set_socks.fd_array[i]);//从集合中删除后要手动释放socket,否则会造成内存泄漏
}
/*关闭网络库*/
WSACleanup();//关闭网络库
}
}
break;
default:
break;
}
return TRUE;
}
int main(int argc, char* argv[])
{
SetConsoleCtrlHandler(fun, TRUE);
/*打开网络库*/
WORD ws_verson = MAKEWORD(2, 2);
WSADATA wd_sock_msg;
int ret = WSAStartup(ws_verson, &wd_sock_msg);
if (0 != ret)
{
switch (ret)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库!");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库!");
break;
case WSAEPROCLIM:
printf("请重新启动!");
break;
case WSAEINPROGRESS:
printf("请尝试关掉不必要的软件,为当前网络运行提供充足资源!");
break;
case WSAEFAULT:
printf("参数错误!");
break;
default:
break;
}
return 0;
}
printf("网络库打开成功!\n");
/*检查版本*/
if (2 != LOBYTE(wd_sock_msg.wVersion) || //地位-》得到主版本
2 != HIBYTE(wd_sock_msg.wVersion)) //高位-》得到副版本
{
//说明版本不对,要关闭网络库
WSACleanup();
return -1;
}
printf("网络库版本号:(%d,%d)\n", LOBYTE(wd_sock_msg.wVersion), HIBYTE(wd_sock_msg.wVersion));
/*创建套接字*/
SOCKET socket_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket_server)
{
printf("socket created failed,error_code:%d\n", WSAGetLastError());
WSACleanup();//关闭网络库
return -1;
}
else
printf("socket created success,return code:%d\n", socket_server);
/*绑定地址*/
struct sockaddr_in si;
si.sin_family = AF_INET;
si.sin_port = htons(12345);
si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int res_bind = bind(socket_server, (const struct sockaddr *) &si, sizeof(si));
if (SOCKET_ERROR == res_bind)
{
printf("bind failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("bind success,return code:%d\n", res_bind);
/*监听listen*/
int res_listen = listen(socket_server, SOMAXCONN);
if (SOCKET_ERROR == res_listen)
{
printf("listen failed,error_code:%d\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();//关闭网络库
return -1;
}
else
printf("listen success,return code:%d\n", res_listen);
/*select模型*/
/*第1步*/
//fd_set fd_set_socks;//装服务端和多个客户端的集合
FD_ZERO(&fd_set_socks);//只将count清0 -> 高效
FD_SET(socket_server, &fd_set_socks);//将服务端装进去
while (1)
{
fd_set fd_set_read_socks = fd_set_socks;
fd_set fd_set_write_socks = fd_set_socks;//里面包含了server
FD_CLR(socket_server, &fd_set_write_socks);//先把服务器删掉
fd_set fd_set_err_socks = fd_set_socks;
struct timeval time_val;
time_val.tv_sec = 3;
time_val.tv_usec = 0;
//第1种情况,检查accept 和 recv
int res_select = select(0, &fd_set_read_socks, &fd_set_write_socks, &fd_set_err_socks, &time_val);
if (0 == res_select)//无响应
{
continue;
}
else if (res_select > 0)//有响应
{
/*处理错误*/
for (u_int i = 0; i < fd_set_err_socks.fd_count; i++)
{
char err_buf[100] = { 0 };
int len = 99;
int res_err = getsockopt(fd_set_err_socks.fd_array[i], SOL_SOCKET, SO_ERROR, err_buf, &len);
if (SOCKET_ERROR == res_err)
{
printf("getsockopt failed, err_code %d\n", WSAGetLastError());
}
else if (0 == res_err)
{
printf("no error happens\n");
}
}
/*判断客户端是否可写*/
for (u_int i = 0; i < fd_set_write_socks.fd_count; i++)
{
//printf("服务器:%d, socket id %d 可写\n", socket_server, fd_set_write_socks.fd_array[i]);
//send
int res_send = send(fd_set_write_socks.fd_array[i], "ok", 2, 0);
if (SOCKET_ERROR == res_send)
{
printf("send error error_code:%d\n", WSAGetLastError());
}
else if (2 == res_send)
{
//printf("发送成功");
}
}
/* 判断响应是服务端产生的还是客户端产生的
响应的是服务端,说明有客户端连接进来 -> accept
响应的是客户端,说明有客户端 下线/向服务端发送消息/错误 -> 具体处理
*/
for (u_int i = 0; i < fd_set_read_socks.fd_count; i++)
{
if (fd_set_read_socks.fd_array[i] == socket_server)//是服务端响应
{
//accept
SOCKET socket_client = accept(socket_server, NULL, NULL);
if (INVALID_SOCKET == socket_client)//连接出错
continue;
FD_SET(socket_client, &fd_set_socks);//链接客户端成功,放进fd_set_socks中
//send
//...
}
else//是客户端响应
{
char str_buf[1500] = { 0 };
int res_recv = recv(fd_set_read_socks.fd_array[i], str_buf, 1499, 0);
if (0 == res_recv)//客户端下线了
{
printf("socket_id: %d 下线\n", fd_set_socks.fd_array[i]);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
else if (res_recv > 0)//接收到了消息
{
printf("服务器socket%d %d接收到了客户端的消息:%s\n",
socket_server, fd_set_socks.fd_array[i], str_buf);
}
else//SOCK_ERROR
{
int err_code = WSAGetLastError();
//客户端强行关闭 出错 error_code 10054
switch (err_code)
{
case 10054:
{
{
printf("client offline,error_code:%d\n", err_code);
//1.从fd_set_socks中删掉
//(先用临时变量接收,再释放),不然有可能会将后面的删掉
SOCKET socket_temp = fd_set_socks.fd_array[i];
FD_CLR(fd_set_socks.fd_array[i], &fd_set_socks);
//2.手动释放
closesocket(socket_temp);
}
}
break;
}
}
}
}
}
else//发生错误了
{
break;
}
}
/*释放所有的socket*/
/*程序结束先关闭socket,再关闭网络库;因为socket是网络库里面的函数*/
for (u_int i = 0; i < fd_set_socks.fd_count; i++)
{
closesocket(fd_set_socks.fd_array[i]);//从集合中删除后要手动释放socket,否则会造成内存泄漏
}
/*关闭网络库*/
WSACleanup();//关闭网络库
printf("\n");
system("pause");
return 0;
}
select模型服务端DEMO