net-doc-基础

ipv4

子网掩码

hzhsan:通俗一点就是,斜杠后面的数字就表示子网掩码,数字具体代表32位子网掩码(二进制形式)中前面的“1”的个数。
而且前面的“ip地址”也不一定是一个ip地址,也可能是一个网络号(末位是0)。
通过后面数字可以将前面的网段进一步细划分成具体的子网。

例如:192.168.1.0/24表示网段是192.168.1.0,子网掩码是24位,子网掩码为:255.255.255.0,用二进制表示为:11111111 11111111 11111111 00000000 ,这里为什么是24呢,就是因为子网掩码里面的前面连续的“1”的个数为24个,一定要连续的才行。 192.168.1.0/28表示的意思是网段是192.168.1.0,子网掩码为:255.255.255.240,用二进制表示为:11111111 11111111 11111111 11110000。

字节序

大端字节序(Big Endian):最高有效位存于最低内存地址处,最低有效位存于最高内存处;
小端字节序(Little Endian):最高有效位存于最高内存地址,最低有效位存于最低内存处。
网络字节序:大端字节序

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int main()
{
int a = 1;
char pc = *(char*)(&a);
if (pc == 1)
printf("第一个字节为1,小端存储\n");
else
printf("第一个字节为0,大端存储\n");

return 0;
}

字符串

字符串是字符数组,字符是占1个字节,所以接收端没有进行大小端转换的话也不会影响最终的字符串值。

TCP_NODELAY

TCP/IP协议中针对TCP默认开启了Nagle算法。Nagle算法通过减少需要传输的数据包,来优化网络。在内核实现中,数据包的发送和接受会先做缓存,分别对应于写缓存和读缓存。

启动TCP_NODELAY,就意味着禁用了Nagle算法,允许小包的发送。对于延时敏感型,同时数据传输量比较小的应用,开启TCP_NODELAY选项无疑是一个正确的选择。比如,对于SSH会话,用户在远程敲击键盘发出指令的速度相对于网络带宽能力来说,绝对不是在一个量级上的,所以数据传输非常少;而又要求用户的输入能够及时获得返回,有较低的延时。如果开启了Nagle算法,就很可能出现频繁的延时,导致用户体验极差。当然,你也可以选择在应用层进行buffer,比如使用java中的buffered stream,尽可能地将大包写入到内核的写缓存进行发送;vectored I/O(writev接口)也是个不错的选择。

对于关闭TCP_NODELAY,则是应用了Nagle算法。数据只有在写缓存中累积到一定量之后,才会被发送出去,这样明显提高了网络利用率(实际传输数据payload与协议头的比例大大提高)。但是这又不可避免地增加了延时;与TCP delayed ack这个特性结合,这个问题会更加显著,延时基本在40ms左右。当然这个问题只有在连续进行两次写操作的时候,才会暴露出来。

连续进行多次对小数据包的写操作,然后进行读操作,本身就不是一个好的网络编程模式;在应用层就应该进行优化。

对于既要求低延时,又有大量小数据传输,还同时想提高网络利用率的应用,大概只能用UDP自己在应用层来实现可靠性保证了。好像企鹅家就是这么干的。

TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。(一个连接会设置MSS参数,因此,TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据)。

Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。

Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。

举个例子:client端调用socket的write操作将一个int型数据(称为A块)写入到网络中,由于此时连接是空闲的(也就是说还没有未被确认的小段),因此这个int型数据会被马上发送到server端,接着,client端又调用write操作写入‘/r/n’(简称B块),这个时候,A块的ACK没有返回,所以可以认为已经存在了一个未被确认的小段,所以B块没有立即被发送,一直等待A块的ACK收到(大概40ms之后),B块才被发送。

这里还隐藏了一个问题,就是A块数据的ACK为什么40ms之后才收到?这是因为TCP/IP中不仅仅有nagle算法,还有一个ACK延迟机制。当Server端收到数据之后,它并不会马上向client端发送ACK,而是会将ACK的发送延迟一段时间(假设为t,大概就是40ms),它希望在t时间内server端会向client端发送应答数据,这样ACK就能够和应答数据一起发送,就像是应答数据捎带着ACK过去。这就解释了为什么'/r/n'(B块)总是在A块之后40ms才发出。

如果你觉着nagle算法太捣乱了,那么可以通过设置TCP_NODELAY将其禁用 。当然,更合理的方案还是应该使用一次大数据的写操作,而不是多次小数据的写操作。