zoukankan      html  css  js  c++  java
  • Python网络编程

    tcp和udp的区别:https://www.jianshu.com/p/c63b082ac565

    1.基于tcp 

     # 服务端
    import socket ​ # 创建服务端socket对象 server = socket.socket() ​ # 绑定IP和端口 server.bind(('192.168.3.6',8000)) ​ # 后边可以等5个人 server.listen(5) ​ print('服务端准备开始接收客户端的连接') # 等待客户端来连接,如果没人来就傻傻的等待。 # conn是客户端和服务端连接的对象,服务端以后要通过该对象进行收发数据。 # addr是客户端的地址信息。 # #### 阻塞,只有有客户端进行连接,则获取客户端连接然后开始进行通信。 conn,addr = server.accept() ​ print('已经有人连接上了,客户端信息:',conn,addr) # 通过对象去获取 # 1024表示:服务端获取数据时,一次性最多拿1024字节。 data = conn.recv(1024) print('已经有人发来消息了',data) # 服务端通过连接对象给客户端回复了一个消息。 conn.send(b'stop') ​ # 与客户端断开连接 conn.close() ​ # 关闭服务端的服务 server.close()
    #客户端
     ​
     import socket
     ​
     client = socket.socket()
     ​
     # 客户端向服务端发起连接请求
     # 阻塞,去连接,直到连接成功后才会继续向下走。
     client.connect(('192.168.3.6',8000))
     # # 链接上服务端后,向服务端发送消息
     client.send(b'hhhhhhhhhhhh')
     # 等待服务端给他发送消息
     data = client.recv(1024)
     print(data)
     ​
     # 关闭自己
     client.close()
    

    1.1 while版 

    #服务端
     ​
     import socket
     ​
     server = socket.socket()
     ​
     server.bind(('192.168.3.6',8001))
     ​
     server.listen(5)
     ​
     while True:
         conn,addr = server.accept()
         # 字节类型
         while True:
             data = conn.recv(1024) # 阻塞
             if data == b'exit':
                 break
             data1=' 梦想成真'
             data1=data1.encode("utf8")
             response = data +data1
             conn.send(response)
     ​
         conn.close()
    #客户端
     ​
     import socket
     ​
     sk = socket.socket()
     ​
     sk.connect(('192.168.3.6',8001))
     ​
     while True:
         name = input("请输入姓名:")
         sk.send(name.encode('utf-8')) # 字节
         if name == 'exit':
             break
     ​
         response = sk.recv(1024) # 字节
         print(response.decode('utf-8'))
     ​
     sk.close()

    2.基于udp

     #服务端
       
     import socket
     sk = socket.socket(type=socket.SOCK_DGRAM)  # 建立一个socket对象,
     # 指定以UDP协议的形式来连接
     sk.bind(('127.0.0.1',8080))
     # 指定服务的地址
     ​
     msg,addr = sk.recvfrom(1024) # 接收消息,发送端的地址
     print(msg,addr)
     sk.sendto(b'HELLO',addr)   # 给发送端回复消息
     ​
     sk.close()  # 关闭socket连接
    #客户端
     ​
     import socket
     ​
     sk = socket.socket(type=socket.SOCK_DGRAM)
     ​
     sk.sendto(b'hello',('127.0.0.1',8080))   # 直接给服务器发送一段消息
     msg,addr = sk.recvfrom(1024)   # 接收对面的回信
     print(msg)
     sk.close()
    

    2.1 udp聊天小工具

    #udp聊天小工具
     #服务端
     import socket
     sk = socket.socket(type=socket.SOCK_DGRAM)
     sk.bind(('127.0.0.1',9090))
     while True:
         msg,addr = sk.recvfrom(1024)
         print('来自[%s:%s]的消息--%s'%(addr[0],addr[1],msg.decode('utf-8')))
     ​
         inp = input('>>>')
         sk.sendto(inp.encode('utf-8'),addr)
     ​
     sk.close()
     #客户端
     ​
     import socket
     sk = socket.socket(type=socket.SOCK_DGRAM)
     addr = ('127.0.0.1',9090)
     while True:
         msg = input('>>>')
         sk.sendto(msg.encode('utf-8'),addr)
         msg_recv,addr = sk.recvfrom(1024)
         print(msg_recv.decode('utf-8'))
     sk.close()
    

    2.2 时间服务器  

    #时间服务器
     ​
     #服务端
     ​
     # 需求
     # 写一个时间同步的服务器
     # 服务端接收请求
     # 按照client端发送的时间格式,将服务器时间转换成对应格式
     # 发送给客户端
     import time
     import socket
     ​
     sk = socket.socket(type=socket.SOCK_DGRAM)
     sk.bind(('127.0.0.1',9000))
     while True:
         msg,addr = sk.recvfrom(1024)
         # msg 客户端发送给server端的时间格式 "%Y-%m-%d %H:%M:%S"
         time_format = msg.decode('utf-8')
         time_str = time.strftime(time_format)
         sk.sendto(time_str.encode('utf-8'),addr)
     sk.close()
    #客户端
     ​
     # client端每隔一段时间发送请求到服务端
     # 发送时间的格式
     import time
     import socket
     sk = socket.socket(type = socket.SOCK_DGRAM)
     sk.sendto('%Y-%m-%d %H:%M:%S'.encode('utf-8'),('127.0.0.1',9000))
     msg,addr = sk.recvfrom(1024)
     print(msg.decode('utf-8'))
     sk.close()  

    3.上传和下载

    3.1 知识补充

     # JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
     # 方法            描述
     # json.dumps()  将 Python 对象编码成 JSON 字符串
     # json.loads()  将已编码的 JSON 字符串解码为 Python 对象
     # json.dump()   将Python内置类型序列化为json对象后写入文件
     # json.load()   读取文件中json形式的字符串元素转化为Python类型
     #服务端
     ​
     import json
     import socket
     ​
     sk = socket.socket()
     sk.bind(('127.0.0.1',8080))
     sk.listen()
     ​
     conn,addr = sk.accept()
     content = conn.recv(1024).decode('utf-8')
     content_dic = json.loads(content)   # json.dumps()函数的使用,将字典转化为字符串 dumps(丢弃)
                                         # json.loads函数的使用,将字符串转化为字典  loads(容器)                                        
     if content_dic['operate'] == 'upload':
         conn.send(b'received!')
         with open(content_dic['filename'],'wb') as f:
             while content_dic['filesize']:
                 file = conn.recv(1024)
                 f.write(file)
                 content_dic['filesize'] -= len(file)
     conn.close()
     sk.close()
    # 客户端
    ​
    import os
    import json
    import socket
    ​
    sk = socket.socket()
    sk.connect(('127.0.0.1', 8080))
    ​
    
    def get_filename(file_path):
        filename = os.path.basename(file_path)
        return filename
    
    ​
    # 选择 操作
    operate = ['upload', 'download']
    for num, opt in enumerate(operate, 1):  # 看下面enumerate函数的用法
        print(num, opt)
    num = int(input('请输入您要做的操作序号 : '))
    if num == 1:
        '''上传操作'''
        # file_path = 'E:python10day33作业.py'
        file_path = input('请输入要上传的文件路径 : ')
        # 告诉对方要上传的文件的名字
        file_name = get_filename(file_path)
        # 读要上传的文件 存成字符串
        with open(file_path, encoding='utf-8') as  f:
            content = f.read()  # content(内容)
        dic = {'operate': 'upload', 'filename': file_name, 'content': content}
        # 将字符串send给server端
        str_dic = json.dumps(dic)
        sk.send(str_dic.encode('utf-8'))
        # server端接收 bytes转码程字符串
        # server端打开文件 写文件
    elif num == 2:
        '''下载操作'''
    sk.close()
    ​
    # 客户端结果
    ​
    # 1 upload
    # 2 download
    # 请输入您要做的操作序号 :
    
    # ===========================================================
    
    # enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)
    # 组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
    ​
    seasons = ['Spring', 'Summer', 'Fall', 'Winter']
    ​
    list(enumerate(seasons))
    [(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
    ​
    list(enumerate(seasons, start=1))  # 下标从 1 开始
    [(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

    4.远程执行系统命令

    4.1 基于tcp

     #基于tcp
     #服务端(服务端向客户端远程执行命令)
       import socket
             sk = socket.socket()
             sk.bind(('127.0.0.1',8090))
             sk.listen()
         conn,addr = sk.accept()
         while True:
             cmd = input('cmd : ')
             if cmd == 'q':
                 conn.send(cmd.encode('utf-8'))
                 break
             conn.send(cmd.encode('utf-8'))
             print('stdout : ',conn.recv(1024).decode('gbk'))
         conn.close()
         sk.close()   
     #客户端
     ​
     import socket
     import subprocess
     sk = socket.socket()
     sk.connect(('127.0.0.1',8090))
     while True:
         cmd = sk.recv(1024).decode('utf-8')
         if cmd == 'q': break
         res = subprocess.Popen(cmd,shell=True,   #管道的数据只能取一次
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE)
         sk.send(res.stdout.read())
         sk.send(res.stderr.read())
     sk.close()
    

    4.1 基于udp

    #基于udp的远程执行系统命令
     #服务端
     ​
     import socket
     sk = socket.socket(type=socket.SOCK_DGRAM)
     sk.bind(('127.0.0.1',8090))
     msg,addr = sk.recvfrom(1024)
     while True:
         cmd = input('cmd : ')
         if cmd == 'q':
             sk.sendto(cmd.encode('utf-8'),addr)
             break
         sk.sendto(cmd.encode('utf-8'),addr)
         print('stdout : ',sk.recvfrom(2048)[0].decode('gbk'))  #接收UDP数据,与recv()类似,
                                                                #但返回值是(data,address)。
                                                                #其中data是包含接收数据的字符串,
                                                                #address是发送数据的套接字地址。
         print('stderr : ',sk.recvfrom(2048)[0].decode('gbk'))
     sk.close()
     ​
    #客户端
     ​
     import socket
     import subprocess
     sk = socket.socket(type=socket.SOCK_DGRAM)
     sk.sendto(b'111',('127.0.0.1',8090))
     while True:
         cmd = sk.recvfrom(1024)[0].decode('utf-8')
         if cmd == 'q': break
         res = subprocess.Popen(cmd,shell=True,
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE)
         sk.sendto(res.stdout.read()*100,('127.0.0.1',8090))
         sk.sendto(res.stderr.read(),('127.0.0.1',8090))
     sk.close()

    5.黏包

    5.1 黏包的产生

     #连续两个send()会出现黏包
     #连续两个recv(),第一个recv()接收很少的数据
     #没接收完的数据会在内存缓存着
     #不能一次性把很多数据放到内存
     #一般一次性传4096字节
     #大文件传输
         #大文件的传输,一定是按照字节读,每一次读固定字节
         #传输过程中,一边读一边传,接收端一边收一边写

    5.2 接收发的缓存引起的黏包 ​

     #服务端
     ​
     from socket import *  #导入包,导入模块只需import+模块即可
     ip_port=('127.0.0.1',8080)
     ​
     tcp_socket_server=socket()
     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'))
     import time
     # time.sleep(1)
     data2=conn.recv(10)
     print('----->',data2.decode('utf-8'))
     conn.close()
     tcp_socket_server.close()
     ​
     #服务端结果
     #-----> he
     #-----> lloegg
     #-----> 
    #客户端
     ​
     import time
     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'))
     # time.sleep(1)
     s.send('egg'.encode('utf-8'))
     ​
    

    5.3 发送发的缓存引起的黏包  

    #服务端
     ​
     from socket import *
     ip_port=('127.0.0.1',8080)
     ​
     tcp_socket_server=socket()
     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()
     ​
     #服务端结果
     #-----> helloegg
     #-----> 
    #客户端
     ​
     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('egg'.encode('utf-8'))
    

    6.黏包问题解决方案

    #为什么会出现黏包现象???
         #只有在tcp协议中才会出现黏包现象,是因为tcp是面向流的
         #tcp协议在发送数据的过程中还有缓存机制来避免数据丢失
         #在连续发送小数据的时候,以及接收大小不符合的时候容易出现黏包现象
         #本质还是因为在接收数据的时候不知道发送数据的大小
         
     #解决黏包的问题
         #不用struct
             #不要连续发或者连续收,发一次收一次
         #用struct
             #先发送数据长度,转换成四个字节,再发数据,接收方先接收4个字节知道长度再接数据
        
     #struct模块
         #struct.pack("i",4096)  i是int,就是把一个数字转换成固定长度的bytes类型
     ​
         #函数                return   explain
         #pack(fmt,v1,v2…)      string   按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回.
         #unpack(fmt,v1,v2…)    tuple    按照给定的格式(fmt)解析字节流,并返回解析结果
    #服务端
     #用的上传和下载的代码
     #现在不只是发数据长度,还发文件路径,文件名,文件大小
     import json
     import struct
     import socket
     ​
     sk = socket.socket()
     sk.bind(('127.0.0.1',8080))
     sk.listen()
     ​
     conn,addr = sk.accept()
     dic_len = conn.recv(4)  # 4个字节 数字的大小
     dic_len = struct.unpack('i',dic_len)[0]       # [0]拿到之前打包的数字
     content = conn.recv(dic_len).decode('utf-8')  # 根据解包的数字拿到报头,报头和字节数字一起发的,
                                                   # 也可以和数据一起发,报头中有数据size                                   
     content_dic = json.loads(content)             # 拿到字典          
     if content_dic['operate'] == 'upload':
         with open(content_dic['filename'],'wb') as f:
             while content_dic['filesize']:
                 file = conn.recv(1024)
                 f.write(file)
                 content_dic['filesize'] -= len(file)
     conn.close()
     sk.close()
     ​
     #客户端
     ​
     import os
     import json
     import struct
     import socket
     ​
     sk = socket.socket()
     sk.connect(('127.0.0.1',8080))
     ​
     def get_filename(file_path):
         filename = os.path.basename(file_path)
         return filename
     ​
     #选择 操作
     operate = ['upload','download']
     for num,opt in enumerate(operate,1):
         print(num,opt)
     num = int(input('请输入您要做的操作序号 : '))
     if num == 1:
         '''上传操作'''
         file_path = input('请输入要上传的文件路径 : ')
         file_size = os.path.getsize(file_path)  # 获取文件大小
         file_name = get_filename(file_path)     # 根据定义的函数拿到文件名
         dic = {'operate': 'upload', 'filename': file_name,'filesize':file_size}
         str_dic = json.dumps(dic).encode('utf-8')
         ret = struct.pack('i', len(str_dic))  # 将字典的大小转换成一个定长(4)的bytes
         sk.send(ret + str_dic)                # 发送字节类型的长度和字典(报头)
         with open(file_path,'rb') as  f:
             while file_size:
                 content = f.read(1024)
                 sk.send(content)
                 file_size -= len(content)
     elif num == 2:
         '''下载操作'''
     sk.close()
    

    7.验证客户端连接的合法性(hashlib)

    # 服务端
    
    import os
    import socket
    import hashlib
    
    
    def check_client(conn):
        secret_key = b'egg'  # 密钥
        send_str = os.urandom(32)  # 随机生成32位的字节 
        # random() 方法返回随机生成的一个实数,它在[0,1)范围内
        conn.send(send_str)
        md5_obj = hashlib.md5(secret_key)
        md5_obj.update(send_str)  # 把send_str更新到md5_obj里面
        secret_ret = md5_obj.hexdigest()  # hash.digest() 返回摘要,作为二进制数据字符串值                                            
        # hash.hexdigest() 返回摘要,作为十六进制数据字符串值                                          
        if conn.recv(1024).decode('utf-8') == secret_ret:
            print('合法的客户端')
            return True
        else:
            print('非法的客户端')
            return False
    
    ​
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8090))
    sk.listen()
    
    conn, addr = sk.accept()
    ret = check_client(conn)
    while ret:
        inp = input('>>>')
        conn.send(inp.encode('utf-8'))
        msg = conn.recv(1024)
        print(msg.decode('utf-8'))
    conn.close()
    sk.close()
    #客户端
     ​
     import socket
     import hashlib
     sk = socket.socket()
     sk.connect(('127.0.0.1',8090))
     ​
     recv = sk.recv(1024)  #接收到的是服务端随机生成的32位字节
     # 用和server端相同的手法对这个字符串进行摘要
     secret_key = b'egon'  # 密钥和服务端相同才是合法的客户端
     md5_obj = hashlib.md5(secret_key)
     md5_obj.update(recv)
     ret = md5_obj.hexdigest()
     sk.send(ret.encode('utf-8'))
     msg = sk.recv(1024)
     if msg:
         print(msg.decode('utf-8'))
         while True:
             inp = input('>>>')
             sk.send(inp.encode('utf-8'))
             msg = sk.recv(1024)
             print(msg.decode('utf-8'))
     sk.close()

    8.验证客户端连接的合法性(hmac)

     #服务端
     ​
     import os
     import socket
     import hmac
     def check_client(conn):
         secret_key = b'egg'  # 密钥
         send_str = os.urandom(32)
         conn.send(send_str)
         hmac_obj = hmac.new(secret_key,send_str)
         secret_ret = hmac_obj.digest()  #bytes类型
         if conn.recv(1024) == secret_ret:
             print('合法的客户端')
             return   True
         else:
             print('非法的客户端')
             return   False
     sk = socket.socket()
     sk.bind(('127.0.0.1',8090))
     sk.listen()
     ​
     conn,addr = sk.accept()
     ret = check_client(conn)
     while ret:
         inp = input('>>>')
         conn.send(inp.encode('utf-8'))
         msg = conn.recv(1024)
         print(msg.decode('utf-8'))
     conn.close()
     sk.close()
     #客户端
     ​
     import socket
     import hmac
     sk = socket.socket()
     sk.connect(('127.0.0.1',8090))
     ​
     recv = sk.recv(1024)
     # 用和server端相同的手法对这个字符串进行摘要
     secret_key = b'egg'  # 密钥
     hmac_obj = hmac.new(secret_key,recv)
     ret = hmac_obj.digest()
     sk.send(ret)
     msg = sk.recv(1024)
     if msg:
         print(msg.decode('utf-8'))
         while True:
             inp = input('>>>')
             sk.send(inp.encode('utf-8'))
             msg = sk.recv(1024)
             print(msg.decode('utf-8'))
     sk.close()
    

    9.socketserver

     # socketserver的介绍
    
     # socketserver是标准库中的一个高级模块
     # socketserver可以简化创建客户端跟创建服务端的代码
    导入模块 import socketserver
    
     # 初始化控制器类Handler-->Handler是一个继承BaseRequestHandler的类Handler中的
         # handle方法决定了每一个连接过来的操作  
     # 控制器类的类名可以是其他的,不一定是Handler,只要继承了BaseRequestHandler就行
     ​
     init():   # 初始化控制设置,初始化连接套接字,地址,处理实例等信息
     handle():# 定义了如何处理每一个连接
     setup(): # 在handle()之前执行,一般用作设置默认之外的连接配置
     finish():# 在handle()之后执行
     ​
     #变量:
     self.request  # 属性是套接字对象,所以使用self.request.xxxx调用套接字的函数
     self.server  # 包含调用处理程序的实例
     self.client_address  # 是客户端地址信息
    
     #服务端
     ​
     import socketserver
     ​
     class Myserver(socketserver.BaseRequestHandler):
         def handle(self):
             # 字节类型
             while 1:
                 # 针对window系统
                 try:
                     print("等待信息")
                     data = self.request.recv(1024)  # 阻塞
                     # 针对linux
                     if len(data) == 0:
                         break
                     if data == b'exit':
                         break
                     response = data + b'SB'
                     self.request.send(response)
                 except Exception as e:
                     break
     ​
             self.request.close()
    # 1 创建socket对象 2 self.socket.bind()  3 self.socket.listen(5)
    server=socketserver.ForkingUDPServer(("127.0.0.1",8899),Myserver)
    
    server.serve_forever()
    
    #============================================================
    
    #客户端
    
    import socket
    
    sk = socket.socket()
    
    sk.connect(('127.0.0.1',8899))
    
    while 1:
        name = input(">>>>:")
        sk.send(name.encode('utf-8')) # 字节
    
        response = sk.recv(1024) # 字节
        print(response.decode('utf-8'))
    
  • 相关阅读:
    hdu1421 搬寝室(dp)
    HDU 2577(DP)
    扩展欧几里德算法
    unique函数的作用
    区间更新 zoj3911
    set的应用
    vue 事件处理器
    vue Class与style绑定
    vue的计算属性
    sass入门
  • 原文地址:https://www.cnblogs.com/yzg-14/p/12204038.html
Copyright © 2011-2022 走看看