在socket通信中,使用select能够达到I/O Multiplexing的效果。理论总是有点让人摸不清头脑,还是看例子好理解。
程序实现功能比较简单,就是“多客户端向服务器发送信息”。
先看实验结果:
启动服务器
启动客户端一
可见客户端一的套接字描述符是4
启动客户端二
可见客户端二的套接字描述符是5
客户端一发送“hello”到服务器
客户端二发送“world”到服务器
关闭客户端一
关闭客户端二
服务器程序如下:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define SERVER_PORT 3456
#define DEBUG 0 //debug macro
#define TRUE 1
#define FALSE 0
int
main(int argc,char **argv){
int listen_sfd,max_sfd,new_sfd;
int on=1,rv,ready_sfd_num,i,connection_close=FALSE,server_end=FALSE;
fd_set master_set,working_set;
struct sockaddr_in sockaddr;
struct timeval timeout;
char buf[100];
/*
* create a AF_INET SOCKET_STREAM socket
* */
listen_sfd=socket(AF_INET,SOCK_STREAM,0);
if(listen_sfd < 0){
perror("server socket ");
exit(EXIT_FAILURE);
}
#if DEBUG
printf("listen_sfd=%d\n",listen_sfd);
#endif
/*
* set socket to SO_REUSEADDR
* */
rv=setsockopt(listen_sfd,SOL_SOCKET,SO_REUSEADDR,(void *)&on,sizeof(on));
if(rv < 0){
perror("server setsockopt");
close(listen_sfd);
exit(EXIT_FAILURE);
}
/*
* set socket to non-blocking
* */
rv=fcntl(listen_sfd,F_SETFL,O_NONBLOCK);
if(rv<0){
perror("server fcntl ");
close(listen_sfd);
exit(EXIT_FAILURE);
}
/*
* bind socket to localhost interface
* */
memset(&sockaddr,0,sizeof(sockaddr));
sockaddr.sin_family =AF_INET;
sockaddr.sin_port =htons(SERVER_PORT);
sockaddr.sin_addr.s_addr=htonl(INADDR_ANY);
rv=bind(listen_sfd,(struct sockaddr *)&sockaddr,sizeof(sockaddr));
if(rv<0){
perror("server bind ");
close(listen_sfd);
exit(EXIT_FAILURE);
}
/*
* set back log to 10
* */
rv=listen(listen_sfd,10);
if(rv<0){
perror("server listen ");
close(listen_sfd);
exit(EXIT_FAILURE);
}
/*
* file descriptors set operations
* */
FD_ZERO(&master_set);
FD_ZERO(&working_set);
max_sfd=listen_sfd;
FD_SET(listen_sfd,&master_set);
/*
* infinite loop of select
* */
while(1){
memcpy(&working_set,&master_set,sizeof(master_set));
/*
* set timeout value to 2 minutes
* */
timeout.tv_sec =2*60;
timeout.tv_usec=0;
ready_sfd_num=select(max_sfd+1,&working_set,NULL,NULL,&timeout);
/*
* select return value has three conditions:
* 1.<0 ---error
* 2.==0---timeout value expires
* 3.>0 ---successful
* */
if(ready_sfd_num<0){
perror("server select ");
break;
}
if(ready_sfd_num==0){
printf("Time Out,End Program!\n");
break;
}
/*
* when select function is invoked successfully,there exists two conditions:
* 1.new connections
* 2.new data
* */
for(i=0;i<=max_sfd && ready_sfd_num>0;++i){
if(FD_ISSET(i,&working_set)){
ready_sfd_num--;
/*
* new connection coming ,accept all incoming connections in queue list
* */
if(i==listen_sfd){
while(1){
new_sfd=accept(listen_sfd,NULL,NULL);
if(new_sfd <0){
if(errno != EWOULDBLOCK){
perror("server accept");
server_end=TRUE;
}
break;
}
if(new_sfd >0){
/*
* set new_sfd to non-blocking
* */
rv=fcntl(new_sfd,F_SETFL,O_NONBLOCK);
if(rv<0){
perror("server fcntl ");
close(new_sfd);
break;
}
FD_SET(new_sfd,&master_set);
if(new_sfd>max_sfd)
max_sfd=new_sfd;
printf("new connection created,socket---%d\n",new_sfd);
}
}
}
/*
* new data coming on connected socket
* */
else{
while(1){
rv=recv(i,buf,sizeof(buf),0);
if(rv<0){
if(errno != EWOULDBLOCK ){
perror("servre recv");
connection_close=TRUE;
}
break;
}
if(rv==0){
connection_close=TRUE;
break;
}
printf("server receive:%s,from socket---%d\n",buf,i);
}
}
if(connection_close){
close(i);
printf("%d socket closed!\n",i);
FD_CLR(i,&master_set);
if(i==max_sfd){
while(FD_ISSET(max_sfd,&master_set)==FALSE)
max_sfd--;
}
}
}
}//end of ready sfd loop
/*determine whether end server or not*/
if(server_end)
break;
}//end of select
/*
* cleanup
* */
while(max_sfd>=0){
if(FD_ISSET(max_sfd,&master_set))//which one :working_set or master_set!!!!master_set
close(max_sfd);
max_sfd--;
}
return 0;
}//end of main
客户端程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#define SERVER_PORT 3456
int
main (int argc, char *argv[]){
int len, rc;
int sockfd;
char send_buf[80];
struct sockaddr_in addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0){
perror("socket");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(SERVER_PORT);
rc = connect(sockfd,
(struct sockaddr *)&addr,
sizeof(struct sockaddr_in));
if (rc < 0){
perror("connect");
close(sockfd);
exit(-1);
}
while(1){
printf("Enter message to be sent:\n");
memset(send_buf,0,sizeof(send_buf));
scanf("%s",send_buf);
// printf("%d\n",strlen(send_buf));
len = send(sockfd, send_buf, strlen(send_buf) + 1, 0);
if (len != strlen(send_buf) + 1){
perror("send");
close(sockfd);
exit(-1);
}
printf("%d bytes sent\n", len);
}
close(sockfd);
return 0;
}