很多天之前都说学习关于select和poll的知识了,但是由于既要工作,又要准备论文。都忙不过来,今天终于能抽出一天的时间把select的相关知识和程序给实现了一遍。
select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。
Select函数的定义:
#include <sys/types.h>
#include <sys/times.h>
#include <sys/select.h>
int select(nfds, readfds, writefds, exceptfds, timeout)
int nfds;
fd_set *readfds, *writefds, *exceptfds;
struct timeval *timeout;
select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他 文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成, 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读写。当readfds或writefds中映象的文件可读或可写或超时,本次select() 就结束返回。程序员利用系统提供的宏FD_ISSET(int fd, fd_set *fdset) 在select()结束时便可判断哪一文件可读或可写。这样我们编写的socket只需要在有东西可读入或写的时候才进行操作。下面给出socket编程中select函数的通用范例:
...
int sockfd, nRet;
fd_set fdR;
struct timeval timeout = ..;
...
for(;;) {
FD_ZERO(&fdR);
FD_SET(sockfd, &fdR);
switch ((nRet =select(sockfd + 1, &fdR, NULL, &timeout))) {
case -1:
error handled by u;
case 0:
timeout hanled by u;
default:
printf(“%d”,nRet); //返回发生变化的句柄的个数
if (FD_ISSET(sockfd)) { //先判断一下socket这外被监视//的句柄是否真的变成可读的了
now u read or recv something;
/* if sockfd is father and
server socket, u can now
accept() */
}
}
}
FD_ISSET(sockfd)就相当通知了sockfd可读。
下面是select的具体实现。主要是参考了faraway的关于select的例子,他的例子实现得真的非常的好,而且下面的评论也给出了很多改进的方法。把这个例子弄懂,那么你对select应该就掌握得差不多了。同时我还把socket通信的两个函数封装了一下,为了以后再写socket通信不再做同类重复的工作。一个是服务器端的建立socket的ServerBuildSocket()函数,还有一个是客户端的ConnectServer()函数。当然我这里的实现都是按照具体一个工程的实现的角度来写的,不再是像以前那样都集成在一个简单的main函数里面。这应该也算是我自己做的第一个从做工程角度去写的实现代码的吧。
config.h
#ifndef _CONFIG_H #define _CONFIG_H //用于控制输出的 #define _DEBUG #ifdef _DEBUG #define TRACE(...) printf(__VA_ARGS__) #else #define TRACE(...) #endif #endif
select_server.c
#include "socketlib.h" #include "config.h" #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> #define MYPORT 4444 #define BACKLOG 5 #define BUF_SIZE 200 int fd_A[BACKLOG]; int conn_amount; int main() { int nSocktfd, nNewSocktfd; int i,nRet,nMaxSock,nSockaddrLen; struct sockaddr_in client_addr; char szBuf[BUF_SIZE]; fd_set fdRead; struct timeval tv; nSocktfd = ServerBuildSocket(MYPORT); if (nSocktfd < 0) { TRACE("ServerBuildSocket faile.\n"); exit(0); } conn_amount = 0; nSockaddrLen = sizeof(client_addr); nMaxSock = nSocktfd; while(1) { FD_ZERO(&fdRead); FD_SET(nSocktfd,&fdRead); tv.tv_sec = 30; tv.tv_usec = 0; //add active connect to fd set for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { FD_SET(fd_A[i],&fdRead); } } nRet = select(nMaxSock + 1, &fdRead, NULL, NULL, &tv); if (nRet < 0) { TRACE("select failed.\n"); break; } else if(nRet == 0) { TRACE("timeout.\n"); continue; } //check whether the connection have data to recieve for (i = 0; i < BACKLOG; i++) { if (fd_A[i] && FD_ISSET(fd_A[i],&fdRead)) { nRet = recv(fd_A[i],szBuf,sizeof(szBuf),0); if (nRet <= 0) { TRACE("Client %d close\n",i); close(fd_A[i]); FD_CLR(fd_A[i],&fdRead); fd_A[i] = 0; conn_amount --; } else { if (nRet < BUF_SIZE) memset(&szBuf[nRet],'\0',1); TRACE("Client[%d] send: %s.\n",i,szBuf); } } }//for //check whether a new connection comes if (FD_ISSET(nSocktfd,&fdRead)) { nNewSocktfd = accept(nSocktfd, (struct sockaddr *)&client_addr,&nSockaddrLen); if (nNewSocktfd <= 0) { TRACE("accept failed.\n"); continue; } if (conn_amount < BACKLOG -1) { for(i = 0; i < BACKLOG; i++) { if(fd_A[i] == 0) { fd_A[i] = nNewSocktfd; conn_amount ++; break; } } TRACE("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); if (nNewSocktfd > nMaxSock) { nMaxSock = nNewSocktfd; } } else { TRACE("max connections arrive,exit\n"); send(nNewSocktfd,"bye",4,0); close(nNewSocktfd); continue; } } }//while //close all connections for (i = 0; i < BACKLOG; i++) { if (fd_A[i] != 0) { close(fd_A[i]); } } exit(0); }
select_client.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include "config.h" #include "socketlib.h" #define MYPORT 4444 int main() { int nSockFd; int nLen; char szBuf[1024]; nSockFd = ConnectServer("127.0.0.1",MYPORT); if (nSockFd < 0) { TRACE("ConnectServer failed.\n"); exit(0); } while(fgets(nSockFd, 1024, stdin) != NULL) { if(send(nSockFd,szBuf,strlen(szBuf),0) <= 0) { TRACE("send failed.\n"); exit(0); } } close(nSockFd); exit(0); }
封装的函数:socketlib.h socketlib.c
#ifndef _SOCKETLIB_H #define _SOCKETLIB_H int ServerBuildSocket(unsigned int nPort); int ConnectServer(const char *szIp,int nPort); #endif
#include "socketlib.h" #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int ServerBuildSocket(unsigned int port) { //Build socket for server ,bind and listen the port int sock_fd,BACKLOG = 5; int nOptVal = 1; struct sockaddr_in server_addr; if ((sock_fd = socket(AF_INET,SOCK_STREAM,0)) == -1) { return -1; } if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,&nOptVal,sizeof(int)) == -1) { return -2; } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { return -3; } if (listen(sock_fd,BACKLOG) == -1) { return -4; } return sock_fd; } int ConnectServer(const char *szIp, int nPort) { int nSockFd; int nAddrLen; struct sockaddr_in addr; if ((nSockFd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { return -1; } addr.sin_family = AF_INET; addr.sin_port = htons(nPort); if (inet_aton(szIp, &addr.sin_addr) < 0) { return -2; } nAddrLen = sizeof(addr); if (connect(nSockFd,(struct sockaddr *)&addr, nAddrLen) == -1) { return -3; } return nSockFd; }
server的makefile:
server:select_server.o socketlib.o gcc -o server select_server.o socketlib.o client:select_client.o socketlib.o gcc -o client select_client.o socketlib.o select_server.o:select_server.c socketlib.h gcc -c select_server.c socketlib.h select_client.o:select_client.c socketlib.h gcc -c select_client.c socketlib.h socketlib.o:socketlib.c socketlib.h gcc -c socketlib.c socketlib.h clean: rm -f select_sever.o socketlib.o