如何发送数据包?
Q:当应用程序产生数据的时候,需要去构造数据包并发送到网络上去,但是由谁负责处理呢?
A:现代操作系统负责数据包得构造与发送,应用程序只需提供数据。 当应用程序产生数据时,应用程序将数据交给OS内核,然后在OS内核添加各层的首部,构建好数据包,然后交给网卡,发送到网络中去。
Q:应用程序如何向OS 发送数据呢?
A: OS为程序提供了一个接口,即socket API,类似于系统调用函数。
Q:通过socket API,只需要提供数据吗?
A:并不,还需要告诉OS 内核,目的端口是什么,传输层使用哪种协议(UDP/TCP),目的IP地址。
Q:为什么不需要告诉源端口?
A:因为源端口是当数据进入到OS内核时,OS会为这个应用程序随机开放一个端口,作为源端口。当服务器端收到数据,进行返回时,也从这个端口进行返回。同样源IP地址,OS内核也是选一个网卡出去,这个网卡的地址作为源IP地址。
详情可见下图:
Q:加工数据是包括什么?
**A:依次包括添加传输层首部,网络层首部,所以在这里需要告诉传输层的目的端口,以及使用的协议,以及网络层的目的IP地址。然后交给网卡驱动程序,构造数据链路层头部,以及物理层。**若想继续详细了解网卡的操作,可参考:网卡工作原理详解,从网卡发送数据再谈TCP/IP协议—网络传输速度计算-网卡构造
了解到上述信息,知道我们要告诉OS 目的IP地址,目的端口,以及数据。
下面是使用python网络编程客户端的代码:
import socketip="127.0.0.1"port=8090data = b"hello PsgQ"sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)sock.sendto(data,(ip,port))
Q: socket函数里面的参数是什么意思?
A: socket.AF_INET表示使用IPv4。socket.SOCK_DGRAM表示传输层使用UDP。
Q:为什么data=b"hello PsgQ"?
A: 因为在网络中传输的是字节流,b前缀将字符串转换成bytes。对于英文字母可以这样转换,但对于中文字符串不行,因为b后面的字符串必须是ascii码可以表示的。若要传递中文字符串,须data=bytes(\’哈哈\’,encoding=\’UTF-8\’)
具体可参考字符串和编码
c语言版代码随后更新。
如何接收数据包?
网卡驱动程序收到数据,作出校验以及检查完相应的目的MAC地址,交到OS内核。
OS内核 网络层模块 进行解封装,查看IP的目的地址,是不是发给自己的,丢弃IP地址不是自己的数据包。然后查看传输层使用的是什么协议,假如是UDP,之后交给UDP模块处理。
UDP获得数据包后,查看目的端口,发给相应的端口。
Q:应用程序怎么去获得数据阿?
A:每个应用程序想要去接收数据,都需要与相应的端口进行绑定。比如HTTP,默认80端口,DNS默认使用53端口等。即OS内核交付给相应的端口,就意味着交付到某个应用程序(通过socket API)。相当于告诉内核,任何时候只要有数据到达这个端口,可以把数据交给我。具体过程是,OS 内核交给相应的端口的缓冲区,这个缓冲区是与端口进行绑定的。应用程序可从这个缓冲区读取数据。
实际情况可能是:当没有数据时,这个应用程序的socket API会被阻塞掉,但当数据包到达时,OS内核会去激活这个进程。
如下图:
从上述信息了解到,我们在server端要进行绑定端口,因为可能有多个网卡,每个网卡IP地址不同,所以我们也要绑定IP地址等。
下面是使用python网络编程服务器端的代码:
import socketip="0.0.0.0"port = 8090sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)sock.bind((ip,port))while True:data,(ip,port)=sock.recvfrom(1024)data=data.decode(\'ascii\')print("clinet:{},port:{}".format(ip,port))print("Received:{}".format(data))
Q: ip ="0.0.0.0"?
A: 表示 数据包的目的地址可以是任意一个网卡的地址。
Q: data=data.decode(\’ascii\’)
A: 因为网络上使用的是字节流,前面客户端将字符串转换成了字节流,在这里使用ascii码进行解码,使字节流重又转换成字符串格式。
注意:调用bind函数 ,参数是(ip,port)
最终运行结果:
c语言版代码随后更新。谢谢大家。