套接字的默认状态是阻塞的。这就意味着当发出一个不能立即完成的套接字调用时,其进程将被投入睡眠,等待相应的操作完成。可能阻塞的套接字调用可分为以下4类:

(1)输入操作,包括read,readv,recv,recvfrom和recvmsg共5个函数。如果某个进程对一个阻塞的TCP套接字(默认设置)调用这些输入函数之一,而且该套接字的接收缓冲区中没有数据可读,该进程将被投入睡眠,直到有一些数据到达。既然TCP是字节流协议,该进程的唤醒就是只要有一些数据到达,这些数据既可能是单个字节,也可能是一个完整的TCP分节中的数据。

既然UDP是数据报协议,如果一个阻塞的UDP套接字的接收缓冲区为空,对它调用输入函数的进程将被投入睡眠,直到有UDP数据报到达。

对于非阻塞的套接字,如果输入操作不能被满足(对于TCP套接字即至少有一个字节的数据报可读,对于UDP套接字即有一个完整的数据报可读),相应调用将立即返回一个EWOULDBLOCK错误

(2)输出操作,包括write,writev,send,sento和sendmsg共5个函数。对于一个TCP套接字,内核将从应用进程的缓冲区到该套接字的发送缓冲区复制数据。对于阻塞的套接字,如果其发送缓冲区中没有空间,进程将被投入睡眠,直到有空间为止。

对于一个非阻塞的TCP套接字,如果其发送缓冲区中根本没有空间,输出函数调用将立即返回一个EWOULDBLOCK错误。如果其发送缓冲区中有一些空间,返回值将是内核能够复制到该缓冲区中的字节数。

UDP套接字不存在真正的发送缓冲区。内核只是复制应用进程数据并把它沿协议栈向下传送,渐次冠以UDP首部和IP首部。因此对一个阻塞的UDP套接字(默认设置),输出函数调用将不会因与TCP套接字一样的原因而阻塞,不过有可能会因为其他的原因而阻塞。

(3)接受外来连接,即accept函数。如果对一个阻塞的套接字调用accept函数,并且尚无新的连接到达,调用进程将被投入睡眠。

如果对一个非阻塞的套接字调用accept函数,并且尚无新的连接到达,accept调用将立即返回一个EWOULDBLOCK错误

(4)发起外出连接,即用于TCP的connect函数。TCP连接的建立涉及一个三次握手过程,而且connect函数一直等到客户收到自己的SYN的ACK为止才返回。这意味着TCP的每个connect总是阻塞其调用进程至少一个到服务器的RTT时间。

如果对一个非阻塞的TCP套接字调用connect,并且连接不能立即建立,那么连接的建立能照样发起(譬如送出TCP三次握手的第一个分组),不过会返回一个EINPROGRESS错误。注意这个错误不同于上述三个情形中的返回的错误。

下面该函数使用fork把当前进程划分成两个进程。

这个函数一开始就调用fork把当前进程划分成一个父进程和一个子进程。子进程把来自服务器的文本行复制到标准输出,父进程把来自标准输入的文本行复制到服务器。

void
str_cli(FILE *fp, int sockfd)
{
pid_t pid;
char sendline[MAXLINE], recvline[MAXLINE]; if ( (pid = fork()) == 0) { /* child: server -> stdout */
while (read(sockfd, recvline, MAXLINE) > 0)
fputs(recvline, stdout); kill(getppid(), SIGTERM); /* in case parent still running */
exit(0);
} /* parent: stdin -> server */
while (fgets(sendline, MAXLINE, fp) != NULL)
writen(sockfd, sendline, strlen(sendline)); shutdown(sockfd, SHUT_WR); /* EOF on stdin, send FIN */
pause();
return;
}

我们知道TCP连接是全双工的,而且父子进程共享同一个套接字:父进程往该套接字中写,子进程从该套接字中读。尽管套接字只有一个,其接收缓冲区和发送缓冲区也分别只有一个,然而这个套接字却有两个描述符在引用它:一个在父进程中,另一个在子进程中。

我们同样需要考虑进程终止序列。正常的终止序列从标准输入上遇到EOF之时开始发生。父进程读入来自标准输入的EOF后调用shutdown发送FIN。但当这发生之后,子进程需继续从服务器到标准输出执行数据复制,直到在套接字上读到EOF。

服务器进程过早终止也有可能发生。要是发生这种情况,子进程将在套接字上读到EOF。这样的子进程必须告知父进程停止从标准输入到套接字复制数据。子进程向父进程发送一个SIGTERM信号,以防止父进程仍在运行。如此处理的另一个手段是子进程无为的终止,使得父进程(如果仍在运行的话)捕获一个SIGCHLD信号。

父进程完成数据复制后调用pause让自己进入睡眠状态,直到捕获一个信号(子进程来的SIGTERM信号),尽管它不主动捕获任何信号。SIGTERM信号的默认行为是终止进程。

UNIX网络编程——非阻塞式I/O(套接字)的更多相关文章

  1. UNIX网络编程——非阻塞connect:时间获取客户程序

    #include "unp.h" int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec) ...

  2. 《Unix 网络编程》08:基本UDP套接字编程

    基本UDP套接字编程 系列文章导航:<Unix 网络编程>笔记 UDP 概述 流程图 recvfrom 和 sendto #include <sys/socket.h> ssi ...

  3. unix网络编程第四章----基于TCP套接字编程

    为了执行网络I/O操作.进程必须做的第一件事情就是调用Socket函数.指定期待的通信协议 #include<sys/socket.h> int socket(int family,int ...

  4. UNIX网络编程-非阻塞connect和非阻塞accept

    1.非阻塞connect 在看了很多资料之后,我自己的理解是:在socket发起一次连接的时候,这个过程需要一段时间来将三次握手的过程走完,如果在网络状况不好或者是其他的一些情况下,这个过程需要比较长 ...

  5. UNIX网络编程——非阻塞accept

    当有一个已完成的连接准备好被accept时,select将作为可读描述符返回该连接的监听套接字.因此,如果我们使用select在某个监听套接字上等待一个外来连接,那就没有必要把监听套接字设置为非阻塞, ...

  6. UNIX网络编程——非阻塞connect: Web客户程序

    非阻塞的connect的实现例子出自Netscape的Web客户程序.客户先建立一个与某个Web服务器的HTTP连接,再获取一个主页.该主页往往含有多个对于其他网页的引用.客户可以使用非阻塞conne ...

  7. UNIX网络编程——非阻塞connect

    当在一个非阻塞的TCP套接字上调用connect时,connect将立即返回一个EINPROGRESS错误,不过已经发起的TCP三次握手继续进行.我们接着使用select检测这个连接或成功或失败的已建 ...

  8. 【UNIX网络编程(四)】TCP套接字编程具体分析

    引言: 套接字编程事实上跟进程间通信有一定的相似性,可能也正由于此.stevens这位大神才会将套接字编程与进程间的通信都归为"网络编程",并分别写成了两本书<UNP1> ...

  9. UNIX网络编程 第8章 基本UDP套接字编程

    UDP是无连接的,不需要accept,TCP通过accept API来接受连接,并且将连接客户端的信息写入到accept将返回的新socket中,该新socket中有服务端和客户端的IP地址和端口,因 ...

随机推荐

  1. POJ 3294 n个串中至少一半的串共享的最长公共子串

    Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 12484   Accepted: 3502 Descr ...

  2. Python与C的简单比较(Python3.0)

    Python可以说是目前最火的语言之一了,人工智能的兴起让Python一夜之间变得家喻户晓,Python号称目前最最简单易学的语言,现在有不少高校开始将Python作为大一新生的入门语言.本萌新也刚开 ...

  3. Python里面 search0和 match0的区别?

    这是正则表达式里面的函数: match()函数只检测RE是不是在string的开始位置匹配,search()会扫描整个string查找匹配: 也就是说match()只有在0位置匹配成功的话才有返回,如 ...

  4. pycharm和shell中的sys.path不一样

    用pip安装了一个模块,在pycharm中不能调用,然后发现shell和pycharm中的sys.path不一样. 纳尼?还能不一样? 很明显左边的pycharm的sys.path中少了三个重要的路径 ...

  5. java实现微信支付之扫码支付

    本文直接从代码调用微信扫码支付讲起.账号配置,参数生成等请参考官方文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 微信 ...

  6. RedisAsyncClientAdapter-------------接口继承

    public abstract class RedisAsyncClientAdapter<K, V, T extends RedisKeyAsyncCommands<K, V> & ...

  7. Axios 使用文档

    Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 使用实例:http://www.cnblogs.com/coolslider/p/7838309.ht ...

  8. Cookie&Seesion会话 共享数据 工作流程 持久化 Servlet三个作用域 会话机制

    Day37 Cookie&Seesion会话 1.1.1 什么是cookie 当用户通过浏览器访问Web服务器时,服务器会给客户端发送一些信息,这些信息都保存在Cookie中.这样,当该浏览器 ...

  9. SpringMVC之简单的增删改查示例(SSM整合)

    本篇文章主要介绍了SpringMVC之简单的增删改查示例(SSM整合),这个例子是基于SpringMVC+Spring+Mybatis实现的.有兴趣的可以了解一下. 虽然已经在做关于SpringMVC ...

  10. kafka Centos7.2 单机集群搭建

    前提是已经安装好了zk集群 1.下载  kafka_2.11-1.0.0.tgz  下载网址 http://kafka.apache.org/documentation.html 2.解压  tar ...