//使用select相比于前面多线程或者多进程而言能更好利用资源,其中最大的一个优点就是在accept函数中原来我们是阻塞等待客户端的连接,而使用select之后我们把套接字放在一个集合中对其进行检查,有客户端连接就直接调用accept函数就好了
//这里就直接把服务端的代码放下面了
/*************************************************************************
> File Name: server.c
> Author: chencj
> Mail: 1378755306@qq.com
> Created Time: 2020年08月04日 星期二 10时41分36秒
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/select.h>
const int N = 128;
int cnt = 0;
int max_scokfd; //最大的文件描述符 创建套接字的时候文件描述符是每次递增1的
void sys_err(char *msg)
{
perror(msg);
exit(1);
}
int main(int argc, char const *argv[])
{
if(argc < 3) {
sys_err("less input\n");
}
int lfd,cfd; //监听套接字 通信套接字
char buf[N];
struct sockaddr_in serve_addr,cli_addr;
lfd = socket(AF_INET,SOCK_STREAM,0);
if(lfd == -1) sys_err("socket error\n");
int vel;
if(setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,(void*)&vel,sizeof(int)) == -1) //设置端口复用
sys_err("setsockopt error\n");
//sockaddr
serve_addr.sin_addr.s_addr = inet_addr(argv[1]);
serve_addr.sin_family = AF_INET;
serve_addr.sin_port = htons(atoi(argv[2]));
socklen_t clen = sizeof(cli_addr);
//bind
if(bind(lfd,(struct sockaddr*)&serve_addr,sizeof(serve_addr)) == -1) sys_err("bind error\n");
//listen
if(listen(lfd,N) == -1) sys_err("listen error\n");
//fd_set
fd_set lset,all_set; //一个用来监听 一个备份使用
FD_ZERO(&lset);FD_ZERO(&all_set);
FD_SET(lfd,&all_set); //把监听套接字放到集合中去
max_scokfd = lfd;
while(1)
{
lset = all_set;
int res;
res = select(max_scokfd+1,&lset,NULL,NULL,0); //res的返回值是看集合中有多少要与服务器进行交互的套接字的数量
if(res < 0) sys_err("select error\n");
for(int i=lfd;i<=max_scokfd;i++)
{
if(FD_ISSET(i,&lset)) //套接字在这个集合中返回1
{
//listen
if(i == lfd) //如果这个套接字等于监听套接字就说明有新的客户端要与服务端进行通信
{
cfd = accept(i,(struct sockaddr*)&cli_addr,&clen); //原来是=多线程或者多进程中是阻塞等待,现在不用阻塞
FD_SET(cfd,&all_set); //把这个套接字加入到集合中去
if(cfd > max_scokfd) max_scokfd = cfd;
cnt++; //表示当前有多少客户端与服务器连接
printf("[ip : %s---port : %d]---%d Connect\n",inet_ntoa(cli_addr.sin_addr),
ntohs(cli_addr.sin_port),cnt);
}
else
{
//进行通信
memset(buf,'\0',sizeof(buf));
int ret;
ret = read(i,buf,sizeof(buf));
if(ret == 0) {
FD_CLR(i,&all_set); //把这个套接字从集合中清除
close(i);
if(max_scokfd == i) max_scokfd--;
cnt--;
}
else
{
printf("[ip : %s---port : %d]---Data:%s\n",inet_ntoa(cli_addr.sin_addr),
ntohs(cli_addr.sin_port),buf);
write(i,"Accept",7);
}
}
}
}
}
close(lfd);
return 0;
}