select在socket中的使用示例

在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;
}






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SelectSocket编程还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。下面详细介绍一下! Select的函数格式(我所说的是Unix系统下的伯克利socket编程,和windows下的有区别,一会儿说明): int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 先说明两个结构体: 第一,struct fd_set可以理解为一个集合,这个集合存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之FD_SET(int ,fd_set *),将一个给定的文件描述符从集合删除FD_CLR(int ,fd_set*),检查集合指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。一会儿举例说明。 第二,struct timeval是一个大家常用的结构,用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。 具体解释select的参数: int maxfdp是一个整数值,是指集合所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows这个参数的值无所谓,可以设置不正确。 fd_set *readfds是指向fd_set结构的指针,这个集合应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件读取数据了,如果这个集合有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。 fd_set *writefds是指向fd_set结构的指针,这个集合应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件写入数据了,如果这个集合有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。 fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。 struct timeval* timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。 返回值: 负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coffee_baba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值