网络编程原理与UDP实现

如何发送数据包?


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  socket

ip="127.0.0.1"
port=8090
data = 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  socket

ip="0.0.0.0"
port = 8090
sock=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)


最终运行结果:

image-20201205135106608



c语言版代码随后更新。谢谢大家。

posted @ 2020-12-05 13:58  PsgQ  阅读(799)  评论(3编辑  收藏  举报