深入学习linux socket编程之select
很多天之前都說學習關于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 #endifselect_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 200int 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 setfor (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 comesif (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 connectionsfor (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 4444int 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_Hint 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 portint 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.ogcc -o client select_client.o socketlib.o select_server.o:select_server.c socketlib.hgcc -c select_server.c socketlib.h select_client.o:select_client.c socketlib.hgcc -c select_client.c socketlib.h socketlib.o:socketlib.c socketlib.hgcc -c socketlib.c socketlib.hclean:rm -f select_sever.o socketlib.o轉載于:https://www.cnblogs.com/Jason-Damon/archive/2013/04/18/3029385.html
總結
以上是生活随笔為你收集整理的深入学习linux socket编程之select的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Android QFIL 烧录
 - 下一篇: Halcon例程(基于3D形状匹配识别方