为什么写一个封闭的TCP套接字比读一个更差?

Rob*_*nes 13 language-agnostic sockets network-programming network-protocols

当您读取一个已关闭的TCP套接字时,您会收到一个常规错误,即它返回0表示EOF或-1,以及一个errno可以打印的错误代码perror.

但是,当您编写一个封闭的TCP套接字时,操作系统会发送SIGPIPE到您的应用程序,如果没有捕获,它将终止应用程序

为什么写封闭的TCP套接字比读取它更糟糕?

Rob*_*nes 12

+1给Greg Hewgill以正确的方向引导我的思考过程以找到答案.

SIGPIPE套接字和管道的真正原因是过滤器习语/模式,它适用于Unix系统中的典型I/O.

从管道开始.像grep这样的过滤程序通常写入STDOUT和读取STDIN,可以由shell重定向到管道.例如:

cat someVeryBigFile | grep foo | doSomeThingErrorProne
Run Code Online (Sandbox Code Playgroud)

shell分叉然后exec的这些程序可能会使用dup2系统调用来重定向STDIN,STDOUTSTDERR使用相应的管道.

由于过滤程序grep不知道并且无法知道它的输出已被重定向,因此如果doSomeThingErrorProne崩溃是一个信号,则告诉它停止写入损坏的管道的唯一方法,因为写入的返回值STDOUT很少,如果有的话检查.

带套接字的模拟inetd服务器将取代shell.

作为一个例子,我假设你可以grep变成一个通过TCP套接字操作的网络服务.例如,inetd如果要grepTCP端口8000 上安装服务器,请将其添加到/etc/services:

grep     8000/tcp   # grep server
Run Code Online (Sandbox Code Playgroud)

然后将其添加到/etc/inetd.conf:

grep  stream tcp nowait root /usr/bin/grep grep foo
Run Code Online (Sandbox Code Playgroud)

发送SIGHUPinetd和通过telnet连接到端口8000.这应当引起inetd对叉,DUP插座上STDIN,STDOUTSTDERR然后exec的grep以foo作为参数.如果你开始在telnet中输入grep行将回显那些包含foo的行.

现在将telnet替换为一个名为的程序ticker,例如将实时股票报价流写入STDOUT并获取命令STDIN.有人telnet到端口8000并输入"start java"来获取Sun Microsystems的报价.然后他们起床去吃午饭.telnet莫名其妙地崩溃了.如果没有SIGPIPE发送,那么ticker将继续永远发送报价,从不知道另一端的进程已经崩溃,并且不必要地浪费系统资源.


Gre*_*ill 10

通常,如果你正在写一个套接字,你会期望另一端正在收听.这有点像电话 - 如果你说话,你不会指望对方只是挂断电话.

如果你正在从套接字读取,那么你期望另一端要么(a)发送一些东西,要么(b)关闭套接字.如果您刚刚向另一端发送了类似QUIT命令的内容,则会发生情况(b).

  • @Robert:Unix上管道输入和输出的典型用例历来是"过滤"程序,它从输入管道读取并写入输出管道(`grep`程序就是这样一个例子).为了在输出不再监听时立即终止这样的过滤器,设置`SIGPIPE`信号默认行为以终止程序.如果没有这个,过滤器将继续写入输出,直到其输入耗尽(这可能是一段时间). (6认同)
  • 告诉我这听起来是否正确:`SIGPIPE`的真正原因是像grep这样的过滤程序通常会写入`STDOUT`,它可能被shell重定向到管道.由于过滤程序不知道并且无法知道它的输出已被重定向,因此告诉它停止写入损坏管道的唯一方法是使用信号,因为很少检查写入`STDOUT`的返回值.模拟插槽将是`inetd`接受连接,产生服务器和`dup2`插槽到`STDIN`,`STDOUT`,`STDERR`! (2认同)

小智 7

将套接字视为发送和接收过程之间的大数据管道.现在假设管道有一个关闭的阀门(插座连接关闭).

如果你正在从插座中读取(试图从管道中取出某些东西),那么尝试阅读那些不存在的东西是没有害处的; 你不会得到任何数据.事实上,正如你所说,你可以得到一个正确的EOF,因为没有更多的数据可供阅读.

但是,这个封闭的连接是另一回事.数据无法通过,您可能最终放弃了一些重要的通信.(你不能用一个关闭的阀门将水送到管道;如果你尝试,某些东西可能会在某处爆裂,或者至少,背压会在整个地方喷水.)这就是为什么有一个更强大的工具提醒您这种情况,即SIGPIPE信号.

您可以随时忽略或阻止信号,但这样做需要您自担风险.