问题描叙:
在 windows 下,用 C 语言写一个 socket 通信的程序,要求作为服务器端的程序能够实现如下功能:
-
检查各个用户是否在线。
-
能够将用户 a 的信息转发到其他用户,可以是一个,也可是多个。
select充当的角色:
在 C 编写该程序的时候遇到这么一个问题,服务器处理信息时是一个循环:a,创建一个新的 socket 然后监听信息;b,处理信息;c,然后释放 socket 连接;循环执行a=>b=>c=>a...。那么有没有办法保存所有的 socket 呢?我的解决思路是用 select。
首先介绍下select这个函数,这是一个文件监听函数,它可以监听文件是否发生改变(准确的说,如果监听的文件是可读文件,那么监听的是该文件是否有新的信息写入,如果监听的文件可写文件,监听的是该文件是否准备好可以往里面写东西了;而且监听的是一个文件的集合。)函数的参数原型不多说,网上很多查的。
其次说一下 scoket,这个网络套接字说白了就是一个文件描叙符,什么是文件描叙符,就是和进程id类似的文件id。管道等通信机制也是文件描叙符,可以用 select 监听。
服务器端的代码:
// 这是多线程编程,这个函数只负责接受消息,不处理。
fd_set fdread; //创建一个文件集合
SOCKET SocketArr[60]; //保存每个链接用户的 socket 的一个数组
SOCKET server_socket; //是保存服务器监听端口的 socket
... // 对服务器网络的初始化,包括初始化 WSA,绑定 ip等等,网上很多,这里不多说,推荐文章: https://www.cnblogs.com/churi/archive/2013/02/27/2935427.html
while (true)
{
FD_ZERO(&fdread); //清楚监听文件集中的所有文件
FD_SET(server_socket,&fdread); //将 server_socket 加入监听文件集
for (i = 0; i < 60; i++) //循环将所有已经链接的客户 socket 加入文件集。
if (SocketArr[i] !=-1)
FD_SET(SocketArr[i],&fdread);
//阻塞式监听,这里只要有一个文件收到信号就不会阻塞
if ((Ret = select(0,&fdread, NULL, NULL, NULL)) == SOCKET_ERROR)
{
printf("selecterrror");
break;
}
if (Ret > 0)
{
//如果服务器端的 socket 发生改变,即收到新建立的连接
if(FD_ISSET(server_socket, &fdread))
{
printf("新连接\n");
//找到一个没有分配的客户socket来保存客户端连接
for (i = 0;i <60; i++)
if(SocketArr[i] == -1)
break;
//用上面找到的没有用的socket来保存客户端连接
if((SocketArr[i] = accept(server_socket, NULL, NULL)) == INVALID_SOCKET)
{
printf("Acceptsocket failed! ");
break;
}
}
printf("消息处理\n");
//如果客户端的 SocketArr 有变化,即受到客户端发来的数据。
for (i = 0; i < 60;i++)
if((SocketArr[i] != -1) && (FD_ISSET(SocketArr[i], &fdread)))
{
//在这里进行消息处理,处理链接为 SocketArr[i],我的处理是将该信息加入到消息队列,让专门处理信息的线程读队列来处理。
}
}
}
客户端代码同我上面推荐的博客的 client 端代码。