Socket工作模式

Socket工作模式
关于linux socket编程的lecture
http://www.aka.org.cn/Lectures/002/Lecture-2.1.8/Lecture-2.1.8/index.htm

阻塞模式

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



你可能感兴趣的:(Socket工作模式)