select服务器端代码:
#include"mylib.h"
/********************************************************************
select应用场合
1、当客户处理多个套接字时--标准IO和网络套接字
2、如果一个TCP服务器既要处理监听套接字又要处理已连接的套接字
3、如果一个服务器既要处理TCP,又要处理UDP
4、如果一个服务器要处理多个服务或者多个协议(inetd)
上百 上千个用户
不管是在网络套接字的场合,还是读写文件描述符,均可以使用
********************************************************************/
int ipv4_tcp_creat_socket(void)
{
int opt;
socklen_t len;
int listenfd;
struct sockaddr_in server;
if((listenfd = socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("Creat socket failed\n");
return -1;
}
//监听套接字,地址可重用
if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))<0)
{
perror("Error,set socket reuse addr failed\n");
return -2;
}
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(SERV_PROT);
//server.sin_addr.s_addr = inet_addr("127.0.0.1");
//inet_pton(AF_INET,"127.0.0.1",&server.sin_addr);
server.sin_addr.s_addr = htonl(INADDR_ANY);
len = sizeof(struct sockaddr);
if(bind(listenfd,(struct sockaddr*)(&server),len)<0)
{
perror("bind error\n");
return -3;
}
listen(listenfd,MAX_LISTEN_QUE);
return listenfd;
}
//阻塞式I/O
//非阻塞式I/O
//I/O复用(select/epoll)
//信号驱动I/O
//异步I/O
int main(int argc,char *argv[])
{
int listenfd,sockfd;
struct sockaddr_in server,client;
socklen_t len;
int bytes = 0;
fd_set global_rdfs,current_rdfs;
int maxfd;
int i;
char read_buf[MAX_BUFFER_SIZE];
char send_buf[MAX_BUFFER_SIZE];
int client_fd[FD_SETSIZE];
len = sizeof(struct sockaddr_in);
//1024
printf("FD_SETSIZE=%d\n",FD_SETSIZE);
listenfd = ipv4_tcp_creat_socket();
FD_ZERO(&global_rdfs);
FD_SET(listenfd,&global_rdfs);
maxfd = listenfd;
//-1:客户端断开
for(i = 0; i < FD_SETSIZE; i++)
client_fd[i] = -1;
while(1)
{
/*
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
fd_set 结构类型 1024/32==32
nfds 要监听的最大的套接字数目+1
readfds select关心的要读数据的套接字组
writefds select关心的要写数据的套接字组
exceptfds 出现异常的套接字组
timeout 不阻塞 调用一次返回一次
永远等待(NULL)直到我关心的套接字有数据读或写
给一定时间等待
*/
/*
//在set中清除fd
void FD_CLR(int fd, fd_set *set);
//检测fd在set中是否有数据可读
int FD_ISSET(int fd, fd_set *set);
//将fd设置在set中
void FD_SET(int fd, fd_set *set);
//set清零
void FD_ZERO(fd_set *set);
*/
current_rdfs = global_rdfs;
if(select(maxfd + 1,¤t_rdfs,NULL,NULL,NULL)<0)
{
perror("select error\n");
return RT_ERR;
}
//监听套接字
if(FD_ISSET(listenfd,¤t_rdfs))
{
if((sockfd = accept(listenfd,(struct sockaddr*)&client,(socklen_t*)&len))<0)
{
perror("accept error\n");
return RT_ERR;
}
printf("sockfd = %d\n",sockfd);
printf("IP:%s,Port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
FD_CLR(listenfd,¤t_rdfs);// listenfd i
//最大的套接字(数字)是当前通信的fd?
maxfd = maxfd > sockfd ? maxfd : sockfd;
//将sockfd放到关注的套接字集合里
FD_SET(sockfd,&global_rdfs);// global_rdfs ? global_rdfs
//continue;
for(i = 0; i < maxfd; i++)
{
//常数放==前面 当少输一个=号时,会报错
if(-1 == client_fd[i])
{
client_fd[i] = sockfd;
break;
}
}
}
//某些已经断开链接的客户端不用检查
//去除0、1、2 标准输入 标准输出 标准错误
//某一次recv读不完数据的解决
//更Z—stack中OSAL的思路有点像
for(i = 0; i <= maxfd; i++)
{
if(-1 == client_fd[i])//说明这个没连接/断开连接
{
continue;
}
if(FD_ISSET(client_fd[i],¤t_rdfs))
{
//不是监听套接字,如:某一个客户端有数据来请求,或者送数据到服务器中
printf("read socket:%d\n",client_fd[i]);
bytes = recv(client_fd[i],read_buf,MAX_BUFFER_SIZE,0);
if(bytes < 0)
{
perror("recv error\n");
return RT_ERR;
}
//最后一个字节FIN 客户端退出
if(bytes == 0)
{
printf("client connect closed\n");
//从关注的套接字集中清除
FD_CLR(client_fd[i],&global_rdfs);
//关闭此套接字 断开了
close(client_fd[i]);
client_fd[i] = -1;
//继续,还有其他客户端要通信
continue;
}
printf("read_buf:%s\n",read_buf);
//strncpy(send_buf,"hello world! \n",strlen("hello world! \n"));
send(client_fd[i],read_buf,strlen(read_buf),0);
//send(client_fd[i],send_buf,strlen(send_buf),0);
memset(read_buf,0,MAX_BUFFER_SIZE);
//memset(send_buf,0,MAX_BUFFER_SIZE);
}
}
}
return 0;
}
实验结果: