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 
|
|
|