简体   繁体   English

在读取所有数据之前写入TCP套接字的行为

[英]Behavior of writing to TCP socket before reading all data

I've been writing small specific purpose HTTP servers for some applications of mine, and I noticed that, if you write() before you read() all available data, the bytes are not sent properly. 我一直在为我的某些应用程序编写小型的专用HTTP服务器,并且我注意到,如果在read()所有可用数据之前write() ,则字节发送不正确。 For example, after read() ing only the request line ( GET / HTTP/1.1\\r\\n ) sent by my browser, I write() : 例如,在read() 浏览器发送的请求行( GET / HTTP/1.1\\r\\n )之后,我执行write()

HTTP/1.1 200 OK\r\n
Connection: close\r\r
Content-Type: text/html\r\n
\r\n
(some HTML stuff)

Wireshark capture of this write() : Wireshark捕获此write()

在此处输入图片说明

'\\n' bytes and Content-Type header are gone! '\\n'字节和Content-Type标头不见了! (Wireshark always displays '\\n' bytes in HTTP header section, if they exist) (如果存在,Wireshark总是在HTTP标头部分显示'\\n'字节)

And the browser do not displays the HTML content. 并且浏览器不显示HTML内容。

So I should never write() before read() everything? 所以,我不应该write()read()的一切吗? Is this the TCP standard? 这是TCP标准吗?

Edit 1: adding my C++ code that sends the stuff: 编辑1:添加发送内容的C ++代码:

string header =
  "HTTP/1.1 200 OK\r\n"
  "Connection: close\r\r"
  "Content-Type: text/html\r\n"
  "\r\n"
;
write(sd, header.c_str(), header.size()); // from unistd.h
FILE* fp = fopen("index.html", "rb");
char by;
while (fread(&by,1,1,fp) == 1) write(sd,&by,1);
fclose(fp);

Edit 2: Well, @selbie pointed a typo... "Connection: close\\r\\r" . 编辑2:好吧,@ selbie指出了一个错字... "Connection: close\\r\\r" After fixing it, the behavior has changed, to a less scary one: the write() is actually not sending data at all. 修复它之后,该行为已改变为一种不太可怕的行为: write()实际上根本没有发送数据。 Now, Wireshark only displays the request! 现在,Wireshark仅显示请求! No response (from my write() ) is captured. 没有响应(来自我的write() )被捕获。

Edit 3: As suggested by @usr, I wrote a small test client... When the server read() s everything before write() ing, the client always receives all the HTTP payload. 编辑3:按照@usr的建议,我编写了一个小型测试客户端...当服务器在write()之前write() read()用作所有内容时,客户端将始终接收所有HTTP有效负载。 When the server write() s before read() ing the headers sent by the client, the client never receives the whole HTTP payload. 当服务器执行write()之前,先执行read()客户端发送的标头,客户端永远不会收到整个HTTP有效负载。 I did many tests! 我做了很多测试!

When the server write() s after read() ing the headers: 当服务器在头文件的read() 之后执行 write()

HTTP/1.1 200 OK\r\n
Connection: close\r\n
Content-Type: text/html\r\n
\r\n
<form>\n
  <input type="text" name="field1" />\n
  <input type="text" name="field2" />\n
  <input type="submit" value="send" />\n
</form>\n

When the server write() s before read() ing the headers: 当服务器在标头的read() 之前执行 write()

HTTP/1.1 200 OK\r\n
Connection: close\r\n
Content-Type: text/html\r\n
\r\n
<form>\n
  <input type="text" name="field1" />\n
  <input

and: 和:

HTTP/1.1 200 OK\r\n
Connection: close\r\n
Content-Type: text/html\r\n
\r\n
<form>\n
  <input type="text" name="field1" />\n
  <input type="text" name="field2" />\n
  <input type="submit"

and: 和:

HTTP/1.1 200 OK\r\n
Connection: close\r\n
Content-Type: text/html\r\n
\r\n
<form>\n
  <input type="tex

I ran the client like 50 times with each setting. 每次设置时,我都将客户端运行50次。

Why is this happening??? 为什么会这样呢??? It has to be something with the kernel... 必须与内核有关...

Edit 4: Another thing I noticed doing these tests... Wireshak always displays both request and response, if the server read() s the headers, but always displays only the request, if the server do not read() the headers. 编辑4:我注意到在做这些测试的另一件事...... Wireshak 始终显示请求和响应,如果服务器read() S中的头部,但始终只显示请求时,如果服务器不read()的标头。 Seriously, this has to do with TCP. 严重的是,这与TCP有关。

You should not write the response before reading the request. 阅读请求之前,您不应该编写响应。 You are violating the HTTP protocol. 您违反了HTTP协议。

That said I don't know why the browser would behave like that. 就是说,我不知道为什么浏览器会表现出这样的行为。 In any case stop violating the HTTP protocol. 无论如何都不要违反HTTP协议。

TCP is a bidirectional stream of bytes. TCP是双向字节流。 It does not care when and what is written. 它不在乎什么时候写什么。 This is not a TCP-level issue. 这不是TCP级别的问题。

I'm not sure what I'm seeing on that screenshot. 我不确定在该屏幕截图上看到的内容。 If you mean the missing \\n chars that certainly was not stripped by the kernel. 如果您是说丢失的\\n字符,那肯定不是内核删除的。 The kernel has no business interfering with the data you are sending. 内核没有业务干扰您发送的数据。 It does not know what the data means. 它不知道数据的含义。

Your app has a bug. 您的应用存在错误。 Maybe you are using some library that "helpfully" converts the line endings to Linux format?! 也许您正在使用一些库来“有帮助”地将行尾转换为Linux格式? Impossible to answer without code. 没有代码就不可能回答。 This answer is as good as it gets with the information posted. 这个答案与发布的信息一样好。

Your Connection header ends with \\r\\r instead of \\r\\n That explains the weirdness in your Wireshark trace. 您的Connection标头以\\r\\r而不是\\r\\n结尾,这说明了Wireshark跟踪中的怪异之处。

Instead of this; 代替这个;

"Connection: close\r\r"

Change it do this: 更改它,执行以下操作:

"Connection: close\r\n"

Well... Seems like the kernel has the following policy, which I discovered only by empirical testing, putting a sleep(1) before close() . 好吧……似乎内核具有以下策略,我只是通过经验测试发现了这一策略,将sleep(1)放在close()之前。

If there is nothing left to be read() , and you immediately call write() and close() , the kernel will send everything properly, no problem. 如果没有要read() ,并且您立即调用write()close() ,内核将正确发送所有内容,没有问题。

But if there is stuff to be read() and you write(); close() 但是如果有东西要read()而你write(); close() write(); close() , the kernel will stop sending the data, like if you just decided to stop the conversation abruptly. write(); close() ,内核将停止发送数据,就像您只是决定突然停止对话一样。 The kernel kinda thinks "well... he didn't even read() this stuff I'm holding and he's closing the socket... he probably also wants me to stop sending this stuff he told me to send". 内核有点想“好吧……他什至没有read()我持有的东西,他正在关闭套接字……他可能还希望我停止发送他告诉我发送的东西”。 Stupid kernel! 愚蠢的内核!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM