socket的缺省方式,也是最常用的方式,即函數阻塞直到調用完畢。 可能造成阻塞的函數有:recvfrom() 、connect()、accept()、讀寫函數、select()、poll()、gethostbyname()等。 ???????????????? 
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/wait.h> #define MYPORT 3490??????????????? ?
/* 監聽的端口 */ #define BACKLOG 10??????????????? ??
/* listen的請求接收隊列長度 *
void main() { int sockfd, new_fd;??????????? /* 監聽端口,數據端口 */ struct sockaddr_in sa;???????? /*
自身的地址信息 */ struct sockaddr_in their_addr; /* 連接對方的地址信息 */ int sin_size;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {?? ??? perror("socket"); ??? exit(1); } sa.sin_family = AF_INET; sa.sin_port = htons(MYPORT);????????
/* 網絡字節順序 */ sa.sin_addr.s_addr = INADDR_ANY;???? /* 自動填本機IP
*/ bzero(&(sa.sin_zero), 8);????????????
/* 其余部分置0 */ if (bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {?? ??? perror("bind"); ??? exit(1); } if (listen(sockfd, BACKLOG) == -1) {?? ??? perror("listen"); ??? exit(1); } /* 主循環 */
while(1) { ??? sin_size = sizeof(struct sockaddr_in); ??? new_fd = accept(sockfd, (struct sockaddr *)&their_addr,
&sin_size)); ??? if (new_fd == -1) { ??? ??? perror("accept"); ??? ??? continue; ??? } ??? printf(”Got connection from %s\n",
inet_ntoa(their_addr.sin_addr)); ??? if (fork() == 0) {??? /* 子進程 */?? ?? ??? ??? if (send(new_fd, "Hello, world!\ n", 14, 0) == -1)?? ?? ??? ??? ??? perror("send"); ??? ??? ??? close(new_fd); ??? ??? ??? exit(0); ??? } ??? close(new_fd);??? /*清除所有子進程 */ ??? while(waitpid(-1,NULL,WNOHANG) > 0); ?} }
非阻塞模式 程序調用可能造成阻塞的函數時: 如果會發生阻塞,這些函數返回-1并將errno設置為EAGAIN或EWOULDBLOCK,程序可繼續向下運行。可能阻塞 的函數對應的任務完成,則再次調用該函數時就返回0表示運行結束。 非
阻塞模式可以避免程序死鎖,但是需要程序不斷檢查各個可能阻塞的函數的狀態,當一個應用程序使用了 非阻塞模式的套接字,它需要使用一個循環來不聽的測試是
否一個文件描述符有數據可讀(稱做polling)。 應用程序不停的polling內核來檢查是否I/O操作已經就緒。這將是一個極浪費CPU資源的操
作,因此不能實際 應用。一般非阻塞模式是與同步I/O模式共同使用的。 
獲得或改變socket的I/O屬性: int ioctl(int sockfd,long cmd,unsigned long* argp); cmd屬性類型,argp屬性的參數。 常用的有: FIONREAD,返回socket緩沖區中未讀數據的字節數 FIONBIO,argp為零時為阻塞模式,非零時為非阻塞模式 SIOCATMARK ,判斷是否有未讀的帶外數據(僅用于TCP協議),返回true或false
int fcntl(int fd, int cmd, long arg); F_SETFL,arp為O_NONBLOCK時進入非阻塞模式,為0時進入阻塞模式。 F_GETFL,獲得屬性。
I/O多路復用(同步I/O模式) ???? 使用select()、poll()等函數實現對多個socket的同步I/O操作。它能同時等待多個 socket描述符,而這些socket描述符其中的任意一個進入讀就緒/寫就緒/出錯狀態,select() 函數就可以返回。 ??? ??  ?????? socket輪詢選擇: int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set
*exceptfds, struct timeval *timeout); 應用于多路同步I/O模式
| FD_ZERO(*set) 清空socket集合 |
| FD_SET(s, *set) 將s加入socket集合 |
| FD_CLR(s, *set) 從socket集合去掉s |
| FD_ISSET(s, *set) 判斷s是否在socket集合 |
???? 常數FD_SETSIZE:集合元素的最多個數 等待選擇機制: int poll(struct pollfd *ufds, unsigned int nfds, int
timeout); 是select機制的一個變種,應用于多路同步I/O模式 ufds是pollfd結構的數組,數組元素個數為nfds。 struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ }; 程序段節選int listenfd, connfd, maxfd=0; int nready; fd_set rset, allset; struct sockaddr_in cliaddr, servaddr; int clilen; listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd > maxfd) maxfd = listenfd; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(4321); bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); FD_ZERO(&allset); FD_SET(listenfd, &allset); listen(listenfd, 10); /* main loop */ while (1) { rset = allset; nready = select(maxfd+1, &rset, NULL, NULL, NULL); if (FD_ISSET(listenfd, &rset)) { /* 有新的客戶端連接請求 clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr*)&cliaddr,&clilen); if (client_num == FD_SETSIZE) { fprintf(stderr, "too many clients\n"); exit(-1); } FD_SET(connfd, &allset); if (connfd > maxfd) maxfd = connfd; if (--nready <= 0) continue; } //以下依次判斷FD_ISSET(某個socket, &rset) 并做相應處理 }
信號驅動I/O ? ?? ?????????????
 異步I/O 
|
|
|