下面的代码是我学习的时候练习用的,说明TCP连接时使用select的好处。
/* select.c: * In nonblock way to communicate with multiple sockets. * This function is to demonstrate the usage of the select. * If the limit number of connections arrive ,then exit */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #define MYPORT 1234 // the port users will be connecting to #define BACKLOG 5 // how many pending connections queue will hold #define BUF_SIZE 200 int fd_A[BACKLOG]; // accepted connedted fd; int conn_amount; // current connection amount; /* Display the amount of socket connections */ void showclient() { int i; printf("client amount: %d\n",conn_amount); for (i = 0; i < BACKLOG; i++) { printf("[%d]:%d ",i, fd_A[i]); } printf("\n\n"); } static void bail(const char *on_what) { perror(on_what); exit(1); } void exit(int sign_no) { /* close other connectioins */ int i; for( i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) close(fd_A[i]); } } int main(int argc,char **argv) { int z; int sock_fd; // listen sock on sock_fd int new_fd; //new connection socket on new_fd struct sockaddr_in server_addr; // server address information struct sockaddr_in client_addr; // client address information socklen_t sin_size; char buf[BUF_SIZE]; int ret; int i; signal(SIGINT, exit); /* Step 1:建立服务器监听socket */ sock_fd = socket(PF_INET, SOCK_STREAM, 0); if(sock_fd == -1) { bail("socket()"); } printf("Server socket() is OK!\n"); /* Step 2:设置socket同地址多次捆绑 */ /* * 缺省条件下,一个套接口不能与一个已在使用中的本地地址捆绑(参见bind())。 * 但有时会需要“重用”地址。因为每一个连接都由本地地址和远端地址的组合唯一确定, * 所以只要远端地址不同,两个套接口与一个地址捆绑并无大碍。 * 为了通知WINDOWS套接口实现不要因为一个地址已被一个套接口使用就不让它与另一个套接口捆绑, * 应用程序可在bind()调用前先设置SO_REUSEADDR选项。请注意仅在bind()调用时该选项才被解释; * 故此无需(但也无害)将一个不会共用地址的套接口设置该选项, * 或者在bind()对这个或其他套接口无影响情况下设置或清除这一选项。 */ int yes = 1; z = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(int)); if( z == -1) { bail("setsockopt()"); } printf("Server setsockopt() is OK!\n"); /* Step3:建立服务器socket地址 */ server_addr.sin_family = AF_INET; // AF_INET server_addr.sin_port = htons(MYPORT); // specified server port in network byte order server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP memset(&server_addr.sin_zero, 0, sizeof server_addr.sin_zero); /* Step4:将地址与socket描述符绑定 */ z = bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); if( z == -1) { bail("bind()"); } printf("Server bind() is OK!\n"); /* Step 5:监听socket连接 * Mark a connection-mode socket,means accepting connectionis*/ z = listen(sock_fd, BACKLOG-3); if( z == -1) { bail("listen()"); } printf("Server listen() is OK!\n"); printf("listen port: %d\n",MYPORT); fd_set fdsr; // a file descriptor set int maxsock; //the biggest of the file descriptor struct timeval tv; // time out conn_amount = 0; sin_size = sizeof client_addr; maxsock = sock_fd; // now it 's just one fd,that's sock_fd while(1) { /* initialize file descriptor set */ /* Step 6:设置监听描述符集合,因为FD_ISSET会清零没有准备好的描述符位,所以每次都要清零后重新设置 */ FD_ZERO(&fdsr); FD_SET(sock_fd,&fdsr); /* time out setting */ tv.tv_sec = 30; tv.tv_usec = 0; int flag = -1; conn_amount = 0; int count = 0; /* Step 7:添加connect成功的socket到fdsr,首次运行时没有,在后面connect */ for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { FD_SET(fd_A[i],&fdsr); conn_amount++; count = i; } else if (fd_A[i] == 0 && flag == -1) flag = i; } // Step 8:监听fdsr,当任意描述符有数据可读时,返回,注意第一个NULL还可以加入fdsw ret = select(maxsock+1, &fdsr, NULL, NULL, &tv); if( ret < 0 ) { bail("select()"); } else if( ret == 0 ) { printf("timeout\n"); continue; } printf("Server select is OK!\n"); /* Step 9:检查每一个描述符,看看谁有活动 */ for( i = 0; i <= count; i++ ) { if(FD_ISSET(fd_A[i], &fdsr)) { ret = recv(fd_A[i], buf, sizeof(buf), 0); if( ret == 0) // 这是客户端关闭时写入socket的信息 { printf("client[%d] close\n",i); close(fd_A[i]); FD_CLR(fd_A[i], &fdsr); fd_A[i] = 0; } else // 这是受到客户端数据时 { if( ret < BUF_SIZE ) { buf[ret] = '\0'; //为什么不这样做?? memset( &buf[ret],'\0', 1); // add a null terminater to the array } printf("client[%d] send:%s\n", i, buf); } } } /* Step 10.检查是否有新连接等待接收?如果有,并且连接数未达最大值,将new_fd加入数组fd_A中 */ if (FD_ISSET(sock_fd, &fdsr)) //fdsr(描述字集)中任何没有准备好的描述子相应位都被清零 { new_fd = accept(sock_fd, (struct sockaddr*)&client_addr,&sin_size); if( new_fd < 0 ) { perror("accept()"); continue; } /* add to fd queue */ if (conn_amount < BACKLOG) { fd_A[flag] = new_fd; printf("new connection client[%d] %s: %d\n", flag, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (new_fd > maxsock) //注意修正最大socket描述符 { maxsock = new_fd; } } else { printf("max connections arrive,exit\n"); send(new_fd, "Sorry! Server is full, bye", BUF_SIZE, 0); close(new_fd); continue; } } showclient(); } return 0; }