通过套接字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()
循环发消息需引入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()
服务端:
# 服务端
# 导入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()
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()
首先我们来看一种情况:
服务端:
# 服务端
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()
如图,本来应该在第二行输出的“欢迎光临”字符串与第一行的“你好”字符串粘连输出,这种现象称为黏包
出现黏包的两种情况:
黏包对比: tcp和udp
tcp协议:
缺点:接收时数据之间无边界,有可能粘合几条数据成一条数据,造成黏包
优点:不限制数据包的大小,稳定传输不丢包
udp协议:
优点:接收时候数据之间有边界,传输速度快,不黏包
缺点:限制数据包的大小(受带宽路由器等因素影响),传输不稳定,可能丢包
应用场景:
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()