• 网络编程之Tcp,udp


    1.TCP

    2.补充异常处理

    3.UDP

    一TCP.
    1.socket基础版本通讯

    服务端:

     1 import socket
     2 
     3 server = socket.socket()# 生成一个对象
     4 server.bind(('127.0.0.1',8080))# 绑定ip和端口
     5 server.listen(5)# 连接池
     6 
     7 conn,addr = server.accept()# 等待别人 一个是ip 一个是地址  阻塞
     8 data = conn.recv(1024) # 听别人说话   阻塞
     9 print(data)
    10 conn.send(b'hello boby')# 给对方回信息
    11 
    12 conn.close()# 断开链接
    13 server.close()# 结束掉进程
    View Code

    客户端:

     1 import socket
     2 
     3 
     4 client = socket.socket()# 生成一个对象
     5 
     6 client.connect(('127.0.0.1',8080))# 发送你的ip和端口号
     7 
     8 client.send(b'hello word')# 对别人说话
     9 data = client.recv(1024) # 听别人说话
    10 print(data)
    11 
    12 client.close()# 断开链接
    View Code

    2 升级版实现多条通讯

    服务端:

     1 import socket
     2 
     3 server = socket.socket() # 创建一个对象
     4 server.bind(('127.0.0.1',8989)) # 绑定 ip 和端口
     5 server.listen(5)# 最大连接数
     6 
     7 while True:
     8     conn,addr = server.accept() # 等待 别人链接进来  ip和端口 # 等到别人来  conn就类似于是双向通道
     9     print(conn)
    10     print(addr)## ('127.0.0.1', 51323) 客户端的地址
    11     while True:
    12         try:
    13             data = conn.recv(1024)# 接收数据
    14             # 判断接收的数据为不为空
    15             if not data:break## b''  针对mac与linux 客户端异常退出之后 服务端不会报错 只会一直收b''
    16             conn.send(data.upper())
    17         except ConnectionResetError as e:
    18             print(e)
    19             break
    20     conn.close()
    View Code

    客户端:

     1 import socket
     2 
     3 client = socket.socket() # 创建一个对象
     4 client.connect(('127.0.0.1',8989))# 写入要链接的ip 和端口号
     5 
     6 while True:
     7     msg = input('请输入:>>>').encode('utf-8')# 发送数据
     8     # 判断数据是否为空
     9     if not msg:continue
    10     client.send(msg)
    11     # 接收数据
    12     data = client.recv(1024)
    13     print(data)
    View Code

    tcp的沾包问题

    客户端:

    import time
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    
    
    client.send(b'hello')
    # time.sleep(5)
    client.send(b'word')

    服务端:

    import socket
    
    server =  socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    conn,addr = server.accept()
    
    data = conn.recv(5)
    print(data)
    data = conn.recv(5)
    print(data)
    
    '''
    可以规定接收的数据长度,但是你不知道对方每次要发多少数据,这个接收数据的1024不能随便改,所以说要想其他的方法
    解决方案就是你要提前告知收方数据的长度
    '''

    那么沾包是如何产生的:

    TCP流式协议, 数据之间没有分界, 就像水  一杯水和一杯牛奶倒在一起了!
    
    UDP 用户数据报协议    
    
    粘包 仅发生在TCP协议中   
    
    1. 发送端 发送的数据量小 并且间隔短 会粘
    2. 接收端 一次性读取了两次数据的内容    会粘 
    3. 接收端 没有接收完整  剩余的内容 和下次发送的粘在一起
    
    无论是那种情况,其根本原因在于  接收端不知道数据到底有多少 
    
    解决方案就是 提前告知接收方 数据的长度  

    那么如何解决沾包:

    .TCP粘包问题
    struct模块 对数据进行打包处理 固定长度
    pack
    unpack

    服务端
    1.生成一个字典
    2.制作该字典的报头
    json序列化
    编码 统计长度
    3.发送字典的报头
    4.发送字典
    5.最后发真实数据

    客户端
    1.先接受固定长度的4个字节字典报头
    2.解析获取字典数据的长度
    unpack(...)[0]
    3.接受字典数据
    解码 反序列化
    4.接受真实数据

    ps:为什么要多加一个字典
    1.打包的数据大小有限
    2.可以携带更多的信息

    代码实现解决沾包问题:
    客户端:

     1 import socket
     2 import struct
     3 import json
     4 
     5 client = socket.socket()
     6 client.connect(('127.0.0.1',8080))
     7 
     8 while True:
     9     msg = input('msg:>>>').encode('utf-8')
    10     if not msg:continue
    11     client.send(msg)
    12     # 接收字典报头
    13     header = client.recv(4)
    14     # 解析报头,拿到真实的数据长度
    15     header_dict = struct.unpack('i',header)[0]
    16     # 接收字典数据
    17     dict_bytes = client.recv(header_dict)
    18     json_dict = json.loads(dict_bytes.decode('utf-8'))
    19     # 从字典中获取信息
    20     print(json_dict)
    21     recv_size = 0
    22     real_data = b''
    23     # 循环接收字典中的信息
    24     while recv_size < json_dict.get('file_size'):
    25         data = client.recv(1024)
    26         recv_size +=len(data)
    27         real_data +=data
    28         print(data.decode('gbk'))
    29         print(recv_size)
    View Code

    服务端:

     1 import socket
     2 import struct
     3 import json
     4 import subprocess
     5 
     6 server = socket.socket()
     7 server.bind(('127.0.0.1',8080))
     8 server.listen(5)
     9 
    10 while True:
    11     conn,addr = server.accept()
    12     while True:
    13         try:
    14             cmd = conn.recv(1024)
    15             if not cmd:break
    16             cmd = cmd.decode('utf-8')
    17             obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    18             res = obj.stdout.read()+obj.stderr.read()
    19             # 定义一个字典
    20             d = {'name':'zy','file_size':len(res)}
    21             json_d = json.dumps(d)
    22             #1.制作字典报头
    23             header = struct.pack('i',len(json_d))
    24             #2. 发送字典报头
    25             conn.send(header)
    26             #3.发送字典
    27             conn.send(json_d.encode('utf-8'))
    28             #4.发送正式数据
    29             conn.send(res)
    30         except ConnectionResetError as e:
    31             print(e)
    32             break
    33     conn.close()
    View Code

    补充:

    解决沾包问题需要用到的struct模块:

     1 import struct
     2 #
     3 # res = 'dgbdushfgcbdsjhgbcshjdgcbidjhgvKADJFCNalskCNkfjcnLJCHSDNCKLSD'
     4 # print('最开始的',len(res))
     5 #
     6 # res1 = struct.pack('i',len(res))
     7 # print('打包好的',len(res1))
     8 #
     9 # res2 = struct.unpack('i',res1)[0] # 如果不加索引解包后的结果就是解包后的 (61,),索引拿到的是0号位
    10 # print('解包后的',res2)
    11 
    12 
    13 
    14 '''
    15  当原始数据特别大的时候 i模式打包不了 需要更换模式?
    16  如果遇到数据量特别大的情况 该如何解决?
    17  可以定义一个字典
    18 '''
    19 d = {
    20     'name':'zy',
    21     'file_size':'123473985961732617417324627346723641746723462374613274164',
    22     'info':'你好',
    23 }
    24 
    25 import json
    26 json_d = json.dumps(d)
    27 print(len(json_d))
    28 
    29 res1 = struct.pack('i',len(json_d))
    30 print(len(res1))
    31 
    32 res2 = struct.unpack('i',res1)[0]
    33 print('解包后的数据',res2)
    View Code

    补充:复习subprocess模块

     1 import subprocess
     2 
     3 while True:
     4     cmd = input('cmg:>>>')
     5     if cmd == 'q':break
     6     if not cmd:continue
     7     obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
     8     print(obj.stdout.read().decode('gbk'))# 正确命令返回的结果
     9     print(obj.stderr.read().decode('gbk'))# 错误的命令返回的结果
    10 
    11 #注意:subprocess获取到的数据,拿完就没有了,不能重复的拿
    View Code

    练习:大文件上传:

    客户端:

     1 mport struct
     2 import json
     3 import socket
     4 import os
     5 
     6 client = socket.socket()
     7 client.connect(('127.0.0.1',8080))
     8 
     9 while True:
    10     # 获取文件路径
    11     MOVIE_DIR = r'H:python视频python10期day29视频'
    12     # 显示文件里的电影
    13     movie_list = os.listdir(MOVIE_DIR)
    14     # 循环打印电影
    15     for i,movie in enumerate(movie_list,1):
    16         print(i,movie)
    17     # 选取电影
    18     choice = input('请选择电影:>>>').strip()
    19     if choice.isdigit():
    20         choice = int(choice)-1
    21         # 判断是否在索引范围内
    22         if choice in range(0,len(movie_list)):
    23             # 获取要上传电影的文件路径
    24             path = movie_list[choice]
    25             # 获取要上传电影的绝对路径,拼接
    26             file_path = os.path.join(MOVIE_DIR,path)
    27             # 获取文件的大小
    28             file_size = os.path.getsize(file_path)
    29             # 定义一个字典
    30             d = {
    31                 'file_name':'qwer.mp4',
    32                 'file_size':file_size,
    33             }
    34             # 序列化字典
    35             json_d = json.dumps(d)
    36             json_bytes = json_d.encode('utf-8')
    37             # 制作字典报头
    38             header = struct.pack('i',len(json_bytes))
    39             # 发送字典报头
    40             client.send(header)
    41             # 发送字典数据
    42             client.send(json_bytes)
    43             # 循环发送文件数据
    44             with open(file_path,'rb') as f:
    45                 for line in f:
    46                     client.send(line)
    47         else:
    48             print('序号不在索引范围内')
    49     else:
    50         print('请输入数字')
    View Code

    服务端:

     1 import socket
     2 import struct
     3 import json
     4 
     5 
     6 server = socket.socket()
     7 server.bind(('127.0.0.1',8080))
     8 server.listen(5)
     9 
    10 while True:
    11     conn,addr = server.accept()
    12     while True:
    13         try:
    14             # 先接收报头
    15             header = conn.recv(4)
    16             # 解析字典报头
    17             header_len =struct.unpack('i',header)[0]
    18             # 接收字典数据
    19             json_bytes = conn.recv(header_len)
    20             json_dict = json.loads(json_bytes.decode('utf-8'))
    21             # 获取字典长度
    22             len_size = json_dict.get('file_size')
    23             # 循环接收文件数据
    24             recv_size = 0
    25             with open(json_dict.get('file_name'),'wb') as f:
    26                 while recv_size < len_size:
    27                     data = conn.recv(1024)
    28                     f.write(data)
    29                     recv_size+=len(data)
    30                 print('上传成功')
    31                 print(recv_size)
    32         except ConnectionResetError as e:
    33             print(e)
    34             break
    35     conn.close()
    View Code

    二.异常处理

    什么是异常处理:

      程序在运行过程中出现了不可预知的错误,并且该程序没有对应的处理机制’,

      那么就会以异常的形式表示出来,造成的影响就是整个程序无法在正常运行了,

      那么我们一般不建议使用异常处理,一般的异常都是可以通过修改代码可以防止

      出现异常的,只有那么你知道会报错,而不能解决的,

      举例:比如说你和别人在打电话,那么可能会出现一些意外,可能手机关机了,或者没信号了

      所以说在无法解决的时候在使用异常处理

    异常的结构:

      1.异常的类型:NAMEERROR

      2.异常的信息:name 'fdsdfsdf' is not defined

      3.异常的位置:Traceback (most recent call last):
      File "D:/python脱产10期视频/day29/01 异常处理.py", line 1, in <module>
      fdsdfsdf

    异常的种类:

      分为俩大类:

        1.语法上的错误:

          是你程序启动立刻就能解决的,这种错误也是不能被容忍的,

          语法上的错误,发现之后应该立即解决

        2.逻辑上的错误:

          这种错误是可以被容忍的,因为一眼看不出来,

          针对逻辑上的错误,可以采用异常处理机制进行捕获

        

        常见的错误类型:

          NAMERROR 名字错误

          SyntaxError 语法错误
          eyError 键不存在
          ValueError 值错误
          IndexError 索引错误

      如何避免:

        异常处理:

          在你认为可能会出现的bug的代码上方try一下:注意try内部的代码块越少越好

          try:

            可能出现的错误

          except 出错的类型 as e: 将报错信息赋值给变量

            出错之后的处理机制

    三.UDP协议

    udp协议简介

    用户数据报协议,是OSI模型中属于传输层的协议

    提供,不可靠的,不要求顺序的,数据量小的,速度快的传输服务

    不可靠:

    发送完成后不需要确认信息 并且立即删除缓存中的数据

    不要求顺序:

    当一个数据较大时 会分为多个数据报来传输,对方无法获知数据的顺序,以及是否完整

    数据量较小的:

    数据越大丢包的可能性越高 ,建议的数据量不要超过1472

    速度快:

    相对于TCP而言快很多 不需要确认信息 ,也不需要建立链接

    udp协议的通讯流程:

    如果TCP比喻为手机打电话的过程 那么UDP可以看做是对讲机

    1.买机器 创建UDP的socket

    2.固定频道 bind一个ip和端口

    3.收发数据 recvfrom sendto

     

    接收

    1.买机器 创建UDP的socket

    2.收发数据 recvfrom sendto

    注意 不能先收 要收数据 必须明确端口号 没有端口号是不可能使用网络服务的

     

     

    TCP 与 UDP 的其他区别

    1.没有连接

    2.不会粘包 每次发送都是一个独立的数据包

     

    TCP 对数据完整性要求较高 : 在线支付 ,文字信息

    UDP: 对数据不要求完整性 但是要快 : 视频 语音 游戏

    udp协议的基本使用:

    服务端:

     1 import socket
     2 
     3 server = socket.socket(type=socket.SOCK_DGRAM)# UDP协议
     4 server.bind(('127.0.0.1',8080))
     5 '''
     6 UDP不需要设置半链接池,它也没有半连接池的概念
     7 因为没有双向通道,不需要accept 直接就是通信循环
     8 '''
     9 while True:
    10     data,addr = server.recvfrom(1024)
    11     print('数据',data)# 客户端发来的信息
    12     print('地址',addr)# 客户端发来的地址
    13     server.sendto(data.upper(),addr)
    14     break
    View Code

    客户端:

     1 import socket
     2 
     3 client = socket.socket(type=socket.SOCK_DGRAM)
     4 #不需要建立链接,直接进入通信循环
     5 server_address = ('127.0.0.1',8080)
     6 
     7 while True:
     8     client.sendto(b'hello',server_address)
     9     data,addr = client.recvfrom(1024)
    10     print('服务端发来的数据',data)
    11     print('服务端发来的地址',addr)
    12     break
    View Code

    udp通讯是不会沾包的:

    服务端:

     1 import socket
     2 
     3 server = socket.socket(type=socket.SOCK_DGRAM)
     4 server.bind(('127.0.0.1',8080))
     5 
     6 data,addr = server.recvfrom(1024)
     7 print(data)
     8 data,addr1 = server.recvfrom(1024)
     9 print(data)
    10 data,addr2 = server.recvfrom(1024)
    11 print(data)
    View Code

    客户端:

    1 import socket
    2 
    3 
    4 client = socket.socket(type=socket.SOCK_DGRAM)
    5 server_address = ('127.0.0.1',8080)
    6 
    7 client.sendto(b'hello',server_address)
    8 client.sendto(b'hello',server_address)
    9 client.sendto(b'hello',server_address)
    View Code

    小练习:udp实现简易版本的qq

    服务端:

     1 import socket
     2 
     3 server = socket.socket(type=socket.SOCK_DGRAM)
     4 server.bind(('127.0.0.1',8080))
     5 
     6 
     7 while True:
     8     data,addr = server.recvfrom(1024)
     9     print(data.decode('utf-8'))
    10     msg = input('msg:>>>').strip()
    11     server.sendto(msg.encode('utf-8'),addr)
    View Code

    客户端1:

     1 import socket
     2 
     3 client = socket.socket(type=socket.SOCK_DGRAM)
     4 server_address = ('127.0.0.1',8080)
     5 
     6 while True:
     7     msg = input('msg:>>>').strip()
     8     msg = '来自客户端1的信息:%s'%msg
     9     client.sendto(msg.encode('utf-8'),server_address)
    10     data,server_addr = client.recvfrom(1024)
    11     print(data.decode('utf-8'))
    View Code

    客户端2:

     1 import socket
     2 
     3 client = socket.socket(type=socket.SOCK_DGRAM)
     4 server_address = ('127.0.0.1',8080)
     5 
     6 while True:
     7     msg = input('msg:>>>').strip()
     8     msg = '来自客户端2的信息:%s'%msg
     9     client.sendto(msg.encode('utf-8'),server_address)
    10     data,server_addr = client.recvfrom(1024)
    11     print(data.decode('utf-8'))
    View Code

    客户端3:

     1 import socket
     2 
     3 client = socket.socket(type=socket.SOCK_DGRAM)
     4 server_address = ('127.0.0.1',8080)
     5 
     6 while True:
     7     msg = input('msg:>>>').strip()
     8     msg = '来自客户端3的信息:%s'%msg
     9     client.sendto(msg.encode('utf-8'),server_address)
    10     data,server_addr = client.recvfrom(1024)
    11     print(data.decode('utf-8'))
    View Code
  • 相关阅读:
    Java之JVM调优案例分析与实战(4)
    Qt浅谈之四十九俄罗斯方块(代码来自网络)
    自作聪明的开发
    Visual Studio 连接 SQL Server 的connectionStringz和
    删除反复行SQL举例
    一起学android之怎样设置TextView中不同字段的字体颜色(22)
    A008-drawable资源
    android 自己定义组件随着手指自己主动画圆
    一个简单的HTML5摇一摇实例
    关于事件的传递机制。
  • 原文地址:https://www.cnblogs.com/zahngyu/p/11324021.html
Copyright © 2020-2023  润新知