[Socket编程]对TCP数据传输有几点疑问

1. socket

对套接字编程的理解还停留在阻塞的阶段,对传输过程中的数据处理不太理解 ,请大家指点一二

但是我不确定怎么传数据好,于是想到两个办法

1.1 一块一块的发,一块一块的送

确定一个定长的char bytes[size]
send:

loop:  
将数据一块一块拷贝进`bytes`,然后发出

recv:

loop:
将数据一块一块从对端读入,然后处理

1.2 告诉对端要发多少,在一起发出去

2.http

我记得http报文是是不定长的,那么对端是怎么知道要接收多少数据??
我特意去问了下老师,他说是用流来处理,不需要向我上面那样用定长数组一块一块处理,我不是很明白

另外

不知道套接字编程的时候需不需要封装字节流,这是我的想法
我想做一个buffer封装要发送或接收的数据

struct buffer {
    unique_ptr<char> data;
    ....
};

http两种处理方式:1. buffer,读到两个空行位置。2. content-length 指明长度。 一般自己直接使用长链接的话,会先传长度。

长链接的话需不需要一块一块地传过去??

如果你问的是底层是不是一块一块的传输的,答案是:是。

如果你问的是需不需要每次write都添加一个自定义长度头,答案是:否。

上面几个回复都没有说道点子上。 TCP传输数据的概念并不是“块”,而是“流” 一个TCP连接就是一个管子,发送放向管子里面塞数据,接收方从管子里面取数据。

发送方不能假设一定能把数据发送出去,因为管子可能会都塞。所以send函数的返回值是这次发出去了几个字节。

接收方也不能假设每次收到的数据大小和发送方是对应的块。因为在传输过程中可能会重新打包数据。

你的1.1/1.2是常用的处理数据边界的方法。

http用的不是你上面说的这两种,而是其它方法:

  1. content-length
  2. chunked transfer encoding

另外,tcp是字节流,每次能发出去多少/接收到多少不完全决定于你bytes的长度。所以你1.1里不能保证一次把size长的数据都发出去,接收端也不一定一次就正好接收到size长的数据。

EDIT: 你最后的问题,从tcp层面来说,不一定需要封装。

流只是一个虚拟的概念,管子也是。加深理解可以,回答具体问题还是别了。别误导别人。 比如:每次数据包经过的节点是一样的么?

tcp做应用层数据传输一般需要应用自己分包,分包的方法有三个:

1 按照(长度+数据)(长度+数据)的方式发送

2 像http那样使用特殊字符分包,比如两个换行算一个包的结束,但数据需要编码(base64等)

3 一个连接传一包,通过断链代表一包发送完成,这个见得比较少,但也不是没有。

流为什么是虚拟的?tcp的数据就是流呀?

这个什么意思?

  • TCP 的话 client 调用 close 就会正常关闭 socket 了,或者异常情况下关闭 socket
  • 网络协议本身规定了如何解析本协议的报文,哪里开始,哪里结束,你只要按照协议标准解析字节流就行了

“流”(stream)是一种抽象,这样对使用 Socket 的用户就不用关心 TCP 协议的通信细节了,只要知道 bind_address/send/recv/close 就行了,对用户来说,他们操作的是“流”。

其实我不怎么明白,在阻塞条件下怎么传输大数据,是直接传呢还是一块一块传
流又是怎么做的??

// client
for data in big_data {
    client_skt.send(data);
}
client_skt.close();
// server
loop {
    data = server_skt.recv();  // blocking
    handle_data(data);
}

这就是我说的一块一块传啊 :joy:

是一块一块传的啊,还是我说的抽象层次的问题。本质上所有的数据都是离散传输的,哪里有“真正的连续”?链路层,传输层都是一个一个的数据包,说“流”就是为了让用户忽略这些细节。

udp使用也不用关心细节,但它不是流。流是一种抽象,但需要协议实实在在的实现它,对于tcp协议,直白的说就是协议里边没有长度字段,udp有长度字段。

对于大数据(Linux小于0x7ffff000)的发送,阻塞模式,tcp可以一次write直接全部发送,也可以拆包循环发送,最大值由操作系统限制。对于udp,一次最大发送64k,最大值由协议限制。

1赞

应用层数据包,tcp数据流,udp报,ip包,tcp包,再加上ip分片,是比较容易绕晕。

蟹蟹回答,了解流很多 :slightly_smiling_face:

如果你说tcp连接是个管子,那么你这里就是在暗示楼主连接一经建立所有包经过的节点的ip是一样的。但是其实是动态的。所以不要使用一些虚拟的带比喻概念的来描述一个事实。

感谢纠正,udp 不是很了解。

tcp可以一次write直接全部发送

你说的一次发送全部数据只是上层接口设计成这样,实现仍然是“切成小块循环发送”,比如 Python 提供的 send 和 send_all,send_all 底层仍然是循环调用 send。

https://docs.python.org/3/library/socket.html#socket.socket.send

更准确的说,能不能一次发完,取决于数据是不是真的所谓的“大块”数据

posix send: https://linux.die.net/man/3/send

If the message is too long to pass through the underlying protocol, send () shall fail and no data shall be transmitted.

linux send 的实现 https://linux.die.net/man/2/send

The maximum control buffer length the kernel can process is limited per socket by the value in /proc/sys/net/core/optmem_max ;

你说的是IP层的问题,我理解TCP层面上不需要关注这个。