Python网络编程

网络编程

  • 一、tcp基本语法
  • 二、tcp循环发消息
  • 三、udp基本语法
  • 四、udp循环发消息
  • 五、黏包
    • 1、黏包
    • 2、struct模块

一、tcp基本语法

通过套接字socket使服务端和客户端进行通讯,为保证数据完整性,tcp协议需进行三次握手,一次只能和一个客户端进行通讯

服务端写法:

# 服务端
#导入套接字模块
import socket
#创建socket对象
sk=socket.socket()
#绑定服务器IP机端口
sk.bind(("127.0.0.1",8080))
#启用服务端监听状态
sk.listen()
#创建tcp三次握手(conn为建立的双向连接对象)
conn,addr=sk.accept()
#接收数据(允许最大传输大小为1024字节)
res=conn.recv(1024)
print(res.decode("utf-8"))
#关闭双向连接
conn.close()
#关闭socket对象
sk.close()

客户端写法:

# 客户端
#导入套接字模块
import socket
#生成套接字对象
sk=socket.socket()
#与服务端建立连接
sk.connect(("127.0.0.1",8080))
#发送消息
sk.send("昨日北京下起了暴雨".encode("utf-8"))
#关闭socket
sk.close()

运行结果:
Python网络编程_第1张图片

二、tcp循环发消息

循环发消息需引入while循环,实现在线聊天功能
服务端:

# 服务端
# 导入套接字模块
import socket
#创建socket对象
sk=socket.socket()
#绑定服务端地址和端口
sk.bind(("127.0.0.1",8090))
#监听端口
sk.listen()
#接收及发送数据
while True:
	conn,addr=sk.accept()
	while True:
		res=conn.recv(1024)
		print(res.decode())
		strvar=input("请输入要发送给客户端的消息:")
		conn.send(strvar.encode())
		if strvar.upper() == "Q":
			break
#关闭双向连接对象
conn.close()
#关闭socket对象
sk.close()

客户端:

# 客户端
#导入socket模块
import socket
#创建socket对象
sk=socket.socket()
#连接服务端
sk.connect(("127.0.0.1",8090))
#发送及接收数据
while True:
	strvar=input("请输入发送给服务端的信息:")
	sk.send(strvar.encode())
	res=sk.recv(1024)
	print(res.decode())
	if res == b"q" or res == b"Q":
		break
sk.close()

运行结果:
服务端:
Python网络编程_第2张图片

客户端:
Python网络编程_第3张图片

三、udp基本语法

服务端:

# 服务端
# 导入socket对象
import socket
# 创建socket对象,由于默认创建的是tcp连接,所以需要加参数
sk=socket.socket(type=socket.SOCK_DGRAM)
# 绑定服务端地址及端口
sk.bind(("127.0.0.1",9005))
# 接收数据
msg,cli_addr=sk.recvfrom(1024)
print(msg.decode())
# 发送数据
strvar="欢迎光临"
sk.sendto(strvar.encode(),cli_addr)
#关闭socket对象
sk.close()

客户端:

# 客户端
import socket
#创建socket对象
sk=socket.socket(type=socket.SOCK_DGRAM)
#发送数据
sk.sendto("你好".encode(),("127.0.0.1",9005))
#接收数据
msg,server_addr=sk.recvfrom(1024)
print(msg.decode())
#关闭连接
sk.close()

运行结果:
服务端:
Python网络编程_第4张图片

客户端:
Python网络编程_第5张图片

四、udp循环发消息

udp协议下,服务端可和多个客户端进行通信
服务端:

# 服务端
import socket
sk=socket.socket(type=socket.SOCK_DGRAM)
sk.bind(("127.0.0.1",9010))
while True:
	msg,cli_addr=sk.recvfrom(1024)
	print(msg.decode())
	strvar=input("请输入要发送给客户端的消息:")
	sk.sendto(strvar.encode(),cli_addr)
sk.close()

客户端1:

# 客户端
import socket
sk=socket.socket(type=socket.SOCK_DGRAM)
while True:
	strvar=input("请输入要向服务端发送的消息:")
	sk.sendto(strvar.encode(),("127.0.0.1",9010))
	msg,server_addr=sk.recvfrom(1024)
	print(msg.decode())
sk.close()

客户端2:

# 客户端
import socket
sk=socket.socket(type=socket.SOCK_DGRAM)
while True:
	strvar=input("请输入要向服务端发送的消息:")
	sk.sendto(strvar.encode(),("127.0.0.1",9010))
	msg,server_addr=sk.recvfrom(1024)
	print(msg.decode())
sk.close()

运行结果:
服务端运行结果:
Python网络编程_第6张图片
客户端1运行结果:
Python网络编程_第7张图片

客户端2运行结果:
Python网络编程_第8张图片

五、黏包

1、黏包

首先我们来看一种情况:
服务端:

# 服务端
import socket
sk=socket.socket()
sk.bind(("127.0.0.1",8080))
sk.listen()
conn,addr=sk.accept()
conn.send("你好".encode())
conn.send("欢迎光临".encode())
conn.close()
sk.close()

客户端:

# 客户端
import socket
sk=socket.socket()
sk.connect(("127.0.0.1",8080))
res1=sk.recv(1024)
print(res1.decode(),"<1>")
res2=sk.recv(1024)
print(res2.decode(),"<2>")
sk.close()

运行结果:
Python网络编程_第9张图片

如图,本来应该在第二行输出的“欢迎光临”字符串与第一行的“你好”字符串粘连输出,这种现象称为黏包

出现黏包的两种情况:

  • 黏包现象一:
    在发送端,由于两个数据短,发送的时间隔较短,所以在发送端形成黏包
  • 黏包现象二:
    在接收端,由于两个数据几乎同时被发送到对方的缓存中,所有在接收端形成了黏包

黏包对比: tcp和udp

tcp协议:
缺点:接收时数据之间无边界,有可能粘合几条数据成一条数据,造成黏包
优点:不限制数据包的大小,稳定传输不丢包

udp协议:
优点:接收时候数据之间有边界,传输速度快,不黏包
缺点:限制数据包的大小(受带宽路由器等因素影响),传输不稳定,可能丢包

2、struct模块

应用场景:

  • 解决黏包场景:
    应用场景在实时通讯时,需要阅读此次发的消息是什么
  • 不需要解决黏包场景:
    下载或者上传文件的时候,最后要把包都结合在一起,黏包无所谓.

pack:
把任意长度数字转化为具有固定4个字节长度的字节流
pack 的范围:-2147483648 ~ 2147483647 21个亿左右
unpack:
i => 把对应的数据转换成int整型
把4个字节值恢复成原来的数字,返回最终的是元组

服务端:

# 服务端
import socket
import struct
sk=socket.socket()
sk.bind(("127.0.0.1",8080))
sk.listen()
conn,addr=sk.accept()
strvar="你好".encode()
res1=len(strvar)
num=struct.pack("i",res1)
#第一次传4位字节流
conn.send(num)
#第二次传真实字节流
conn.send(strvar)
conn.send("欢迎光临".encode())
conn.close()
sk.close()

客户端:

# 客户端
import socket
import struct
sk=socket.socket()
sk.connect(("127.0.0.1",8080))
res1=sk.recv(1024)
#解包,n为传入数据真实长度
n=struct.unpack("i",res1)
#按字符串真实长度卡死边界,注意n是元组
res2=sk.recv(n[0])
print(res2.decode(),"<1>")
res3=sk.recv(1024)
print(res3.decode(),"<2>")
sk.close()

客户端运行结果:
因为第一次传包用struct模块卡死了边界,所以不会出现黏包现象
Python网络编程_第10张图片

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