网络编程|网络编程——socket套接字、黏包


文章目录

  • 一、socket套接字
    • 1.socket套接字类型
    • 2.socket使用
      • 2.1 服务端
      • 2.2客户端
    • 3.通信循环
    • 4.链接循环
    • 5.半连接池
    • 6.补充
  • 二、黏包
    • 1.黏包的产生
    • 2.黏包解决

一、socket套接字 1.socket套接字类型
  1. 基于文件类型的套接字家族
    套接字家族的名字:AF_UNIX
    unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
  2. 基于网络类型的套接字家族
    套接字家族的名字:AF_INET
    (还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
2.socket使用 网络编程|网络编程——socket套接字、黏包
文章图片

2.1 服务端
import socketserver = socket.socket()# 建立服务器 """ 括号内不写参数默认就是基于网络的遵循TCP协议的套接字 """ server.bind(('127.0.0.1', 8080))# 设置ip和端口 """ 服务端应该具备的特征 固定的地址 ... 127.0.0.1是计算机的本地回环地址 只有当前计算机本身可以访问 """ server.listen(5)# 开机 """ 半连接池(暂且忽略 先直接写 后面讲) """ sock, addr = server.accept()# 等待并接听电话没有人来就原地等待(程序阻塞) """ listen和accept对应TCP三次握手服务端的两个状态 """ print(addr)# 客户端的地址 data = https://www.it610.com/article/sock.recv(1024)# 接收客户端消息 print(data.decode('utf8')) sock.send('你好啊'.encode('utf8'))# 发送消息给客户端 """ recv和send接收和发送的都是bytes类型的数据 """ sock.close()# 关闭连接 server.close()# 关闭服务器

2.2客户端
import socketclient = socket.socket()# 产生一个socket对象 client.connect(('127.0.0.1', 8080))# 根据服务端的地址链接 while True: sc = input("需要发给服务器的消息:").strip() client.send(sc.encode("utf8"))# 给服务端发送消息 data = https://www.it610.com/article/client.recv(1024)# 接收服务端回复的消息 print(data.decode('utf8'))client.close()# 关闭客户端

3.通信循环 由于服务器和客户端之间需要不断通信,所以需要对消息的接收和发送循环起来
# 下方为服务器端循环通信,由于是先recv所以会先等待客户端发消息 while True: data = https://www.it610.com/article/sock.recv(1024)# 接收客户端消息 print("客户端:" + data.decode('utf8')) sc = input("输入需要发给客户端的消息:").strip() sock.send(sc.encode('utf8'))# 发送消息给客户端# 下方为客户端循环通信,由于是先send所以需要先发消息,然后等待服务器消息 while True: sc = input("需要发给服务器的消息:").strip() client.send(b'hello sweet heart!!!')# 给服务端发送消息 data = https://www.it610.com/article/client.recv(1024)# 接收服务端回复的消息 print(data.decode('utf8'))

4.链接循环 当客户端断开后需要让服务器进入accept等待下一个客户端,此时需要使用到异常处理
如果是windows 客户端异常退出之后服务端会直接报错,使用以下方式重新等待连接:
处理方式
异常处理
如果是mac或linux 服务端会接收到一个空消息,使用一下方法重新等待连接:
处理方式
len判断
循环链接的要点在于客户端断开后服务端需要重新等待客户端连接
while True: try: data = https://www.it610.com/article/sock.recv(1024)# 接收客户端消息 print("客户端:" + data.decode('utf8')) sc = input("输入需要发给客户端的消息:").strip() sock.send(sc.encode('utf8'))# 发送消息给客户端 except Exception: sock, addr = server.accept() print(addr)

5.半连接池 半连接池:限制的是同一时刻的请求数,而非连接数,第一次握手成功时客户端会连上半连接池
设置的最大等待人数 >>>: 节省资源 提高效率
py文件默认同一时间只能运行一次 如果想单独分开运行多次
listen(5)

6.补充 反复重启服务端可能会报错>>>:address in use
这个错在苹果电脑报的频繁 windows频率较少
from socket import SOL_SOCKET,SO_REUSEADDR server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 在bind前加

二、黏包 1.黏包的产生 会发生黏包的两种情况:
  1. 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
  2. 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
2.黏包解决 使用struct模块,精确获取数据的大小,避免数据太小,产生黏包
【网络编程|网络编程——socket套接字、黏包】代码如下(示例):
# struct模块 import structdata1 = 'hello world!' print(len(data1))# 12 res1 = struct.pack('i', len(data1))# 第一个参数是格式 写i就可以了 print(len(res1))# 4 ret1 = struct.unpack('i', res1) print(ret1)# (12,)data2 = 'hello baby baby baby baby baby baby baby baby' print(len(data2))# 45 res2 = struct.pack('i', len(data2)) print(len(res2))# 4 ret2 = struct.unpack('i', res2) print(ret2)# (45,)

    推荐阅读