基于TCP,UDP协议的套接字通信以及SocketServer模块

网络编程

粘包现象

1.当发送数据大小大于接收数据时,在下次接收时会接收到上次内有接收完的数据

2.Nagle算法,将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包(TCP协议无消息保护边界)

基于TCP协议的套接字通讯

服务端

import socket
import subprocess
import struct
import json
ip_port = ('127.0.0.1',9000)
serve = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
serve.bind(ip_port)
serve.listen(5)
while True:
    conn,addr = serve.accept()
    while True:
        try:
            cmd = conn.recv(1024)
            if not cmd:break
            obj = subprocess.Popen(cmd.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE
                                   )
            stdout = obj.stdout.read()
            stderr = obj.stderr.read()
            data_size = len(stdout+stderr)
            head_dic = {
                'title':'asasd.txt',
                'data_size':data_size
            }
            head_dic_str = json.dumps(head_dic)
            head_dic_str_len = len(head_dic_str)
            pack_head = struct.pack('i',head_dic_str_len)
            conn.send(pack_head)
            conn.send(head_dic_str.encode('utf-8'))
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break
    conn.close()
serve.close()


客户端

import socket
import struct
import json
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 9000))
while True:

    cmd = input('>>>:')
    if not cmd: continue
    client.send(cmd.encode('utf-8'))
    print('发送成功')
    # 拿到被打包  报头的长度 (被打包成四位bytes字符串)
    pack_head_dic_len = client.recv(4)
    # 解包获得具体  字典的长度 元组(,)
    head_dic_len = struct.unpack('i',pack_head_dic_len)
    # 拿到字典的字节形式
    head_dic_bys = client.recv(head_dic_len[0])
    # 拿到字典的字符串形式
    head_dic_str = head_dic_bys.decode('utf-8')
    # 反序列化字符串形式的字典
    head_dic = json.loads(head_dic_str)
    # 取得传输文本的具体大小
    data_size = head_dic['data_size']
    # 循环接收数  并拼接bytes类型字符串
    data_file = b''
    current_size = 0
    while current_size<data_size:
        data = client.recv(1024)
        current_size += len(data)
        data_file = b"%s%s"%(data_file,data)
    print(data_file.decode('gbk'))

基于UDP协议的套接字通讯

UDP协议

UDP叫数据报协议,意味着发消息都带有数据报头
udp的server不需要就行监听也不需要建立连接
在启动服务之后只能被动的等待客户端发送消息过来,客户端发送消息的时候,要带上服务端的地址
服务端在回复消息的时候,也需要带上客户端的地址

特点

  1. udp协议客户端允许发空
  2. udp协议不会粘包
  3. udp协议服务端不存在的情况下,客户端照样不会报错
  4. udp协议支持并发

服务端

import socket
serve = socket.socket(type=socket.SOCK_DGRAM)
serve.bind(('127.0.0.1',9009))
while True:
    data,addr = serve.recvfrom(1024)
    print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' % (addr[0], addr[1], data.decode('utf-8')))
    msg = input('回复消息:').strip().encode('utf-8')
    serve.sendto(msg,addr)

服务端

import socket
client = socket.socket(type=socket.SOCK_DGRAM)
friend_dic = {
    'aaa':('127.0.0.1',9009),
    'bbb':('127.0.0.1',9009),
    'ccc':('127.0.0.1',9009),
}
while True:
    friend_name = input('请输入对方的名称:').strip()
    while True:
        msg = input('发送消息:').strip()
        if msg=='q':break

        client.sendto(msg.encode('utf-8'),friend_dic[friend_name])
        rec_msg = client.recvfrom(1024)
        print('%s回复一条消息:%s'%(friend_name,rec_msg[0].decode('utf-8')))


小知识:windows电脑和max电脑的时间同步功能,其实就是基于udp朝windows,max服务器发送请求获取标准时间

SocketServer模块

# TCP socketserver使用
import socketserver
class MyTcpServer(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                data = self.request.recv(1024)  # 对于tcp,self.request相当于conn对象
                if len(data) == 0:break
                print(data)
                self.request.send(data.upper())
            except ConnectionResetError:
                break
if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(('127.0.0.1',8081),MyTcpServer)
    server.serve_forever()

# UDP socketserver使用
import socketserver


class MyUdpServer(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            data, sock = self.request
            print(data)
            sock.sendto(data.upper(), self.client_address)


if __name__ == '__main__':
    server = socketserver.ThreadingUDPServer(('127.0.0.1', 8080), MyUdpServer)
    server.serve_forever()
posted @ 2019-05-05 15:50  会飞的空心菜  阅读(155)  评论(0)    收藏  举报