Python基础37 基于tcp、udp套字编程、粘包现象、struct模块

发布时间 2023-07-04 20:56:50作者: 初学程序员

基于tcp协议的套接字编程(sochet编程)

什么是socket?

  通常翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把tcp/ip层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中。

套接字分类:

AF_UNIX:用在局域网中

AF_INET:用在互联网中

客户端和服务端启动顺序

  先启动服务端,等待客户端来连接,然后接受客户端发来的消息,进行通讯

套接字工作流程

 简易版的套接字编程

服务端
import socket

# 实例化socket类
server = socket.socket()
# 服务端绑定信息
server.bind(('127.0.0.1', 8000))
# 监听消息、半连接池
server.listen(3)
# 代码停住,等待客户端发来消息
sock, addr = server.accept()
# 接收消息
data = sock.recv(1024)
print('客户端发来消息:%s' % data.decode())
# 回应客户端收到消息
sock.send(data)
# 断开与客户的联系
sock.close()
# 关机
server.close()



客户端
import socket

client = socket.socket()
# 与服务端建立连接
client.connect(('127.0.0.1', 8000))
# 输入发送信息
inp = input('请输入你要发送的信息:')
# 发送信息
client.send(inp.encode('utf8'))
# 接收服务端发来信息
data = client.recv(1024)
print('服务端发来:%s' % data.decode())
# 关闭连接
client.close()

加上通讯循环

服务端
import socket

# 实例化socket类
server = socket.socket()
# 服务端绑定信息
server.bind(('127.0.0.1', 8001))
# 监听消息、半连接池
server.listen(3)
# 代码停住,等待客户端发来消息
while True:
    sock, addr = server.accept()
    # 接收消息
    data = sock.recv(1024)
    print('客户端发来消息:%s' % data.decode())
    # 回应客户端收到消息
    sock.send(data)
    # 断开与客户的联系
    sock.close()
# 关机
server.close()



客户端
import socket

client = socket.socket()
# 与服务端建立连接
client.connect(('127.0.0.1', 8001))
# 输入发送信息
inp = input('请输入你要发送的信息:')
# 发送信息
client.send(inp.encode('utf8'))
# 接收服务端发来信息
data = client.recv(1024)
print('服务端发来:%s' % data.decode())
# 关闭连接
client.close()

加上连接循环

服务端
import socket

# 实例化socket类
server = socket.socket()
# 服务端绑定信息
server.bind(('127.0.0.1', 8012))
# 监听消息、半连接池
server.listen(3)
# 代码停住,等待客户端发来消息
while True:
    sock, addr = server.accept()  # 代码走到这里会停住,等待接收客户端发来的消息

    """
    sock:代表的是当前客户端的连接对象
    addr:代表的是客户端的信息(ip+port)
    """
    while True:
        try:
            # 5.真正的取到客户端发来的消息
            data = sock.recv(1024)  # 接收的最大数据,字节类型
            if len(data) == 0:
                break
            print("客户端发来的消息:%s" % data)
            # 6. 服务端个客户端发消息
            sock.send(data.upper())  # 数据类型必须是字节类型
        except ConnectionResetError as e:
            print(e)
            break
    # 7. 断开与客户端之间的连接
    sock.close()
# 8. 关机
server.close()



客户端
import socket

client = socket.socket()
# 与服务端建立连接
client.connect(('127.0.0.1', 8012))
# 输入发送信息
while True:
    inp = input('请输入你要发送的信息:')
    if inp == 'q':
        break
    if len(inp) == 0:
        continue
    # 发送信息
    client.send(inp.encode('utf8'))
    # 接收服务端发来信息
    data = client.recv(1024)
    print('服务端发来:%s' % data.decode())

基于UDP协议的套接字编程

服务端

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8202))
while True:
    data, addr = server.recvfrom(1024)
    print('接收到客户端信息》》》:%s、%s' % (data.decode(), addr))
    server.sendto(data, addr)

server.close()


客户端
import socket

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 数据报协议-》UDP

while True:
    msg = input('>>: ').strip()
    if msg == 'q':
        break
    if msg == '':
        continue
    client.sendto(msg.encode('utf-8'), ('127.0.0.1', 8202))
    data, server_addr = client.recvfrom(1024)
    print(data.decode())

client.close()

粘包现象

其实就是网络·传输里面的数据没有被完全取出来

TCP的特性:流式协议
  当客户端发送的数据量很小并且时间间隔很小的时候
  它会把数据打包成一个包一次性发送

发生粘包两种情况

1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

服务端

from socket import *
ip_port=('127.0.0.1',8080)

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)


conn,addr=tcp_socket_server.accept()


data1=conn.recv(10)
data2=conn.recv(10)

print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))

conn.close()

客户端

import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)


s.send('hello'.encode('utf-8'))
s.send('feng'.encode('utf-8'))

2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

服务端

from socket import *
ip_port=('127.0.0.1',8080)

tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)


conn,addr=tcp_socket_server.accept()


data1=conn.recv(2) #一次没有收完整
data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的

print('----->',data1.decode('utf-8'))
print('----->',data2.decode('utf-8'))

conn.close()

客户端'

import socket
BUFSIZE=1024
ip_port=('127.0.0.1',8080)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(ip_port)


s.send('hello feng'.encode('utf-8'))

拆包的发生情况

  当发送端缓冲区的长度大于网卡的MTU(网络能够传输的最大数据包大小,以字节为单位)时,tcp会将这次发送的数据拆成几个数据包发送出去

解决粘包问题

 

struct模块

#_*_coding:utf-8_*_
#http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html
__author__ = 'ly'
import struct
import binascii
import ctypes

values1 = (1, 'abc'.encode('utf-8'), 2.7)
values2 = ('defg'.encode('utf-8'),101)
s1 = struct.Struct('I3sf')
s2 = struct.Struct('4sI')

print(s1.size,s2.size)
prebuffer=ctypes.create_string_buffer(s1.size+s2.size)
print('Before : ',binascii.hexlify(prebuffer))
# t=binascii.hexlify('asdfaf'.encode('utf-8'))
# print(t)


s1.pack_into(prebuffer,0,*values1)
s2.pack_into(prebuffer,s1.size,*values2)

print('After pack',binascii.hexlify(prebuffer))
print(s1.unpack_from(prebuffer,0))
print(s2.unpack_from(prebuffer,s1.size))

s3=struct.Struct('ii')
s3.pack_into(prebuffer,0,123,123)
print('After pack',binascii.hexlify(prebuffer))
print(s3.unpack_from(prebuffer,0))