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


一、socket套接字

1.socket套接字类型

  1. 基于文件类型的套接字家族
    套接字家族的名字:AF_UNIX
    unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

  2. 基于网络类型的套接字家族
    套接字家族的名字:AF_INET
    (还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

2.socket使用

在这里插入图片描述

2.1 服务端

import socket

server = 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 = sock.recv(1024)  # 接收客户端消息
print(data.decode('utf8'))
sock.send('你好啊'.encode('utf8'))  # 发送消息给客户端
"""
recv和send接收和发送的都是bytes类型的数据
"""
sock.close()  # 关闭连接
server.close()  # 关闭服务器

2.2客户端

import socket


client = socket.socket()  # 产生一个socket对象
client.connect(('127.0.0.1', 8080))  # 根据服务端的地址链接
while True:
    sc = input("需要发给服务器的消息:").strip()
    client.send(sc.encode("utf8"))  # 给服务端发送消息
    data = client.recv(1024)  # 接收服务端回复的消息
    print(data.decode('utf8'))

client.close()  # 关闭客户端

3.通信循环

由于服务器和客户端之间需要不断通信,所以需要对消息的接收和发送循环起来

# 下方为服务器端循环通信,由于是先recv所以会先等待客户端发消息
while True:
    data = 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 = client.recv(1024)  # 接收服务端回复的消息
    print(data.decode('utf8'))

4.链接循环

当客户端断开后需要让服务器进入accept等待下一个客户端,此时需要使用到异常处理

如果是windows 客户端异常退出之后服务端会直接报错,使用以下方式重新等待连接:
处理方式
异常处理

如果是mac或linux 服务端会接收到一个空消息,使用一下方法重新等待连接:
处理方式
len判断
循环链接的要点在于客户端断开后服务端需要重新等待客户端连接

while True:
    try:
        data = 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模块,精确获取数据的大小,避免数据太小,产生黏包

代码如下(示例):

# struct模块
import struct

data1 = '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,)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值