一.select()函数
select()函数准备好读的条件:
1>.套接口有数据可读
2>.该连接的读这一半关闭(也就是接收了FIN的TCP连接)。对这样的套接口进行读操作将不阻塞并返回0(也就是返回EOF)。
3>.该套接口是一个侦听套接口且已完成的连接数不为0。
4>.其上有一个套接口错误待处理,对这样的套接口的读操作将不阻塞并返回-1,并设置errno,可以通过设置SO_ERROR选项调用getsockopt函数获得。
select()函数准备好写的条件:
1>.套接口有可用于写的空间。
2>.该连接的写这一半关闭,对这样的套接口进行写操作将产生SIGPIPE信号。
3>.该套接口使用非阻塞的方式connect建立连接,并且连接已经异步建立,或则connect已经以失败告终。
4>.其上有一个套接口错误待处理。
二.accept()函数
1.阻塞模式
阻塞模式下调用accept()函数,而且没有新连接时,进程会进入睡眠状态。
2.非阻塞模式
非阻塞模式下调用accept()函数,而且没有新连接时,将返回EWOULDBLOCK错误。
非阻塞模式select() + accept()
- sockfd = listen_tcp();
- FD_SET(sockfd, rset);
- while(1){
-
ret = select(sockfd + 1, rset, NULL, NULL, timeout);
- if(select()返回TIMEOUT){
- printf("日志打印");
- sleep(1);
- continue;
- }
- else if(FD_ISSET(sockfd,&rset)){
- connfd = accept(sockfd, ...);
- }
- else if(select()返回错误){
- return -1;
- }
-
- pthread_create(thread_recv_data, connfd, ...);
-
- close();
-
- }
三.connect()函数
1.阻塞模式
客户端调用connect()函数将激发TCP的三路握手过程,但仅在连接建立成功或出错时才返回。返回的错误可能有以下几种情况:
1>.如果TCP客户端没有接收到SYN分节的响应,则返回ETIMEDOUT,阻塞模式的超时时间在75秒(4.4BSD内核)到几分钟之间。
2>.如果对客户的SYN的响应时RST,则表明该服务器主机在我们指定的端口上没有进程在等待与之连接(例如服务器进程也许没有启动),这称为硬错,客
户一接收到RST,马上就返回错误ECONNREFUSED.
3>.如果某客户发出的SYN在中间的路由器上引发了一个目的地不可达ICMP错误,多次尝试发送失败后返回错误号为EHOSTUNREACH或ENETUNREACH.
附加:产生RST的三种情况,一是SYN到达某端口但此端口上没有正在侦听的服务器、二是TCP想取消一个已有连接、三是TCP接收了一个根本不存在的连接上的
分节。
2.非阻塞工作模式
调用connect()函数会立刻返回EINPROCESS错误,但TCP通信的三路握手过程正在进行,所以可以使用select函数来检查这个连接是否建立成功。
源自Berkeley的实现有两条与select函数和非阻塞相关的规则:
1>.当连接成功建立时,描述字变成可写。
2>.当连接建立出错时,描述字变成即可读又可写。getsockopt()函数的errno == 0表示只可写。
非阻塞模式 select() + connect()
- while(1){
- ret = connect();
- if(errno == EINPROCESS){
- select(...)
- if(FD_ISSET(sockfd,&wset) ){
- getsockopt(...);
- if(errno == 0){
- }
- }
- else if(select()返回TIMEOUT){
- sleep(1);
- continue();
- }
- }
- else if(ret == -1){
-
- }
- pthread_create(thread_send_log, ...);
- close();
- }
(hanchaoman) |