python网络通信(二)——基于UDP网络编程

目录

 一、socket

二、传输协议

三、UDP数据发送

发送数据

接收数据

四、echo服务器

五、聊天程序


 一、socket

python网络通信(二)——基于UDP网络编程_第1张图片

       (该图片单单只为了学习一个英语单词)

         socket也叫做套接字,是应用层和传输层之间的桥梁,利用socket我们可以通过网络完成进程间通信:

python网络通信(二)——基于UDP网络编程_第2张图片

         应用层封装好数据给socket,socket会将数据传递给传输层,经过一层层的传输,通过网络传给另一台电脑,再经过一层层解析,解析到socket后,socket将数据给应用层。

        socket本质是编程接口(API),是对TCP/IP协议的封装,封装成的socket更容易使用TCP/IP协议,它是面向客户-服务器模型设计的,针对客户和服务器的程序提供了不同的系统调用。

        服务器可以看做大型的计算机,功能更强,数据大部分都存放在服务器里,可以在自己的电脑或者手机下载一个客户端,客户端里面存放的数据量并不大,比如说,手机下载一个爱奇艺的客户端,想下载视频,那么需要socket发送一个请求到服务器,服务器接收到信号之后再将数据传输给手机,手机才能下载。

        socket(套接字)之间的连接可以分为三个步骤:服务器监听(服务器等待别人连接)、客户端请求(告诉服务器自己想干什么)、连接确认(双方确认连接)。

        接下来创建一个套接字对象:

from socket import *#导入套接字模块
s = socket(AF_INET,SOCK_DGRAM)#s是一个socket对象,拥有发送和接收网络数据的能力

        函数里有两个参数必须写:AF_INET规定使用IPV4协议用于Internet进程间的通信,下一个参数可以是SOCK_STREAM(TCP协议)或者SOCK_DGRAM(UDP协议)

        ---TCP慢但是稳定不会丢失数据

        ---UDP快但是可能会丢失数据,被黑客攻击

二、传输协议

        UDP——User Datagram Protocol,用户数据报协议。

        是无连接的、简单的、面向数据报的传输层协议,在发送数据时,不会跟对方建立连接,且不确定对方是否能收到,只是简单的将数据传输过去,并不能保证能不能到达目的地,因为UDP传输数据时不用建立连接,并且没有超时重发等机制,所以传输数据速度很快,适用于QQ之类的。

        TCP——Transmission Control Protocol,传输控制协议

        是一个面向连接的协议,就是说建立连接时,必须确定建立了一个连接,一个TCP的连接必须经过三次对话,主机A想跟主机B对话,则主机A向主机B发送一个连接请求的数据包,接着主机B向主机A发送一个同意的连接,主机A收到后,向B发送一个数据包,确认主机B的要求,三次对话完成,然后才传输正式的数据。TCP占用的资源更多结构更复杂,TCP可以更保证数据的正确性,可以保证数据传输的顺序。

        网络通信时,双方的协议应该一样。

三、UDP数据发送

发送数据

        sendto函数可以实现数据发送,函数中添加元组,元组里是IP地址和端口号,通过IP地址和端口号找到接收方。(代码中的IP和端口是从网络调试助手看到的)

from socket import *

s = socket(AF_INET,SOCK_DGRAM)#IPv4协议,UDP协议
s.sendto("hello",("192.168.0.106",8080))#向IP是192.168.0.106的8080端口发送hello

        我们利用网络调试助手接收数据,协议是UDP,跟发送方相同,端口号默认可用动态端口(8080),可以换成其他可用端口,编程中做相应的修改即可,点击打开。

python网络通信(二)——基于UDP网络编程_第3张图片

 程序运行后会报错:

python网络通信(二)——基于UDP网络编程_第4张图片

因为网络之间数据发送要使用字节流,应使用encode(),就可以将前面的字符串转换为字节流 :

s.sendto("hello".encode(),("192.168.0.106",8080))

修改后会如下:

python网络通信(二)——基于UDP网络编程_第5张图片

 通过网络调试助手可以看到发送方的网络地址和端口号:

python网络通信(二)——基于UDP网络编程_第6张图片

因为程序没有绑定端口号,所以每次运行程序发送数据的端口可能会变。

        如果发送中文,会发现乱码,因为python中文默认是UTF-8编码,而网络调试助手是GB2312编码,发送的数据编码和解码不一样,所以会乱码,所以我们的python指定使用GB2312编码:

from socket import *

s = socket(AF_INET,SOCK_DGRAM)#IPv4协议,UDP协议
s.sendto("你好".encode("gb2312"),("192.168.0.106",8080))

然后就可以正常的发送信息:

python网络通信(二)——基于UDP网络编程_第7张图片

可以将地址、内容都赋值给变量,进行优化 :

from socket import *

s = socket(AF_INET,SOCK_DGRAM)#IPv4协议,UDP协议
addr = ("192.168.0.106",8080)
date = input("请输入内容:")

s.sendto(date.encode("gb2312"),addr)

效果如下:

python网络通信(二)——基于UDP网络编程_第8张图片

接收数据

 如果想使用VS接收数据,则

s.recvfrom(1024)

程序运行到这里时会阻塞,等待接收数据,1024表示本次接收所能接收的最大字节数,程序接收完后应该:

s.close()

因为对象s是占用一定空间的,当用不到s后,应该及时的关闭。代码如下:

from socket import *#导入模块

addr = ("192.168.0.106",8080)#准备接收方的地址

s = socket(AF_INET,SOCK_DGRAM)
s.sendto("你好鸭".encode("gb2312"),addr)#发送内容,转换为字节流
redate = s.recvfrom(1024)
print(redate)
s.close()

运行结果如下:

python网络通信(二)——基于UDP网络编程_第9张图片

网络调试助手接收到信息后会自动得到对方的地址(如图中的远程主机),所以网络调试助手发送数据时知道往哪里发送。

收到的数据是一个元组,第一个元素是内容,开头是“b”,表示是一个字节流,想得到收到的消息,应该:

print(redate[0].decode())#0是第一个元素,decode转换为字符串

又因为网络调试助手是GB2312编码,所以应该指定为GB2312解码:

print(redate[0].decode("gb2312"))

效果如下:

python网络通信(二)——基于UDP网络编程_第10张图片

 ps:如果是字符串常量,在字符串前加”b”,效果跟加encode一样

s.sendto(b"hhhh",addr)

 内容可以是 ASCII范围内的字符和其它十六进制形式的字符数据,但不能是中文等非ASCII字符。

前面是网络端口先接受数据,这样网络端口发送数据时才能知道往哪里发,如果是网络端口直接发送,则编程时应该先绑定一个IP地址和端口号:

s.bind(("",8888))

IP会自动是本机IP,所以没必要指定。但发现网络调试助手不能主动跟别人发送数据,所以还是应该主动给网络调试助手发送一个数据,此时,得到发送数据的端口就是8888。

四、echo服务器

        echo服务器是非常有用的用于调试和检测的工具,协议的作用也很简单,就是接收到什么原样发回。

        新建一个python文件写入:

from socket import *#导入模块

udpSock = socket(AF_INET,SOCK_DGRAM)
udpSock.bind(("",8888))#绑定一个端口
while True:#使其不停的做收发
    recvData = udpSock.recvfrom(1024)#首先接收发送来的数据
    udpSock.sendto(recvData[0],recvData[1])#将数据发送回去
udpSock.close()

原程序为:

from socket import *#导入模块

s = socket(AF_INET,SOCK_DGRAM)
s.bind(("",7777))
addr = ("113.54.204.147",8888)
s.sendto("hello,你好".encode(),addr)
redata = s.recvfrom(1024)
print(redata[0].decode())

都运行之后会发现发送什么,就会返还什么,程序的作用就是可以测试网络通不通。

五、聊天程序

一个文件写入:

from socket import *#导入模块

udpSock = socket(AF_INET,SOCK_DGRAM)
udpSock.bind(("",8888))#绑定一个端口
addr = ("113.54.204.147",7777)
while True:
    recvData = udpSock.recvfrom(1024)
    print(recvData[0].decode())
    data = input("想说啥:")
    udpSock.sendto(data.encode(),addr)
udpSock.close()

另一个写入:

from socket import *#导入模块

s = socket(AF_INET,SOCK_DGRAM)
s.bind(("",7777))
addr = ("113.54.204.147",8888)
while True:
    data = input(":")
    s.sendto(data.encode(),addr)
    recvData = s.recvfrom(1024)
    print(recvData[0].decode())
s.close()

因为都是python编写,用的是utf-8,所以不用再指定。

此时是一个半双工程序,就是说收发消息不能同时进行,因为执行接收时会阻塞,不能发送消息。

其实可以通过并发编程来实现,但是在多进程中不能使用input()。

你可能感兴趣的:(Python,udp,python)