zoukankan      html  css  js  c++  java
  • 网络编程(三)

    网络编程(三)    >>>思维导图>>>中二青年

    基于TCP协议使用socket分发大文件

    案例:电影上传

    思路:客户端端循环一行一行读文件并一行一行传输,服务端循环接收并写入文件

    客户端代码

    import socket,json,os,struct
    client = cocket.socket()
    client.connet(('127.0.0.1',8080))
    while True:
        # 获取电影类表,循环展示
        MOVIE_DIR = r'D:路径视频'
        movie_list = os.listdir(MOVIE_DIR)
        for i,movie in enumerate(movie_list,1):
            print(i,movie)
        # 用户选择
        choice = input('please choice movie to upload>>>:')
        # 判断是否是数字
        if choice.isdigit():
            # 将字符串转为int
            choice = in(choice) - 1
            # 判断用户选择在不在列表范围内
            if choice in range(0,len(movie_list))
               # 获取用户想上传的文件路径
                path = movie_list[choice]
               # 拼接文件的绝对路径
               file_path = os.path.join(MOVIE_DIR,path)
               # 获取文件的大小
               file_size = os.path.getsize(file_path)
               # 定义一个字典
               res_d = {
                          'file_name':'性感荷官在线发牌.,mp4',
                          'file_size':file_size,
                          'msg':'注意身体,多喝营养快线'
               }
               # 序列话字典
               json_d = json.dunps(res_d)
               json_bytes = json_d.encode('utf-8')
               # 1.先制作字典格式的报头
               header = struct.pack('i',len(json_bytes))
               # 2.发送字典的报头
               client.send(header)
               # 3.再发字典
               client.send(json_bytes)
               # 4.再发文件数据(打开文件循环发送)
               with open(file_path,r'b') as f:
                   for line in f:
                       client.send(line)
            else:
               print('not in rang')
        else:
            print('must be a number')
    

    服务端代码

    import socket,os,json,struct
    server = socket,socket()
    sever.bind(('127.0.0.1',8080))
    server.listen(5)
    while True:
        conn,addr = server.accept()
        while True:
            try:
                header_len = conn.recv(4)
                # 解析字典报头
                header_len = struct.unpack('i',hendaer_len)[0]
                # 再接收字典数据
                header_dic = conn.recv(header_len)
                real_dic = json.loads(header_dic.decode('utf-8'))
                # 获取数据长度
                total_size = real_dic.get('file_size')
                # 循环接受并写入文件
                recv_size = 0
                with open(real_dic.get('file_size'),'wb;) as f:
                    while recv_size < total_size:
                        data = conn.recv(1024)
                        f.write(data)
                        recv_size += len(data)
                    print('上传成功!')
            except ConnetionResetError as e:
                print(e)
                break
        conn.close()
    

    UDP协议

    UDP是一个简单的传输层协议。和TCP相比,UDP有下面几个显著特性:

    1.UDP缺乏可靠性

    UDP 本身不提供确认,序列号,超时重传等机制。UDP 数据报可能在网络中被复制,被重新排序。即 UDP 不保证数据报会到达其最终目的地,也不保证各个数据报的先后顺序,也不保证每个数据报只到达一次

    2.UDP 数据报是有长度的

    每个 UDP 数据报都有长度,如果一个数据报正确地到达目的地,那么该数据报的长度将随数据一起传递给接收方。而 TCP 是一个字节流协议,没有任何(协议上的)记录边界。

    3.UDP 是无连接的

    UDP 客户和服务器之前不必存在长期的关系。UDP 发送数据报之前也不需要经过握手创建连接的过程。

    4.UDP 支持多播和广播

    基于UDP协议的socket

    dp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接

    server端

    import socket
    udp_sk = socket.socket(type=socket.SOCK_DGRAM)   # 创建一个服务器的套接字
    udp_sk.bind(('127.0.0.1',9000))        # 绑定服务器套接字
    msg,addr = udp_sk.recvfrom(1024)
    print(msg)
    udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
    udp_sk.close()                         # 关闭服务器套接字
    

    client端

    import socket
    ip_port=('127.0.0.1',9000)
    udp_sk=socket.socket(type=socket.SOCK_DGRAM)
    udp_sk.sendto(b'hello',ip_port)
    back_msg,addr=udp_sk.recvfrom(1024)
    print(back_msg.decode('utf-8'),addr)
    

    UDP不会发生黏包

    UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。 
    不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。 
    对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。 
    不可靠不黏包的udp协议:udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y;x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠。

    粘包现象只发生在tcp协议中

    1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。
    2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

    udp和tcp一次发送数据长度的限制

    用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535- IP头(20) – UDP头(8)=65507字节。用sendto函数发送数据时,如果发送数据长度大于该值,则函数会返回错误。(丢弃这个包,不进行发送) 
    用TCP协议发送时,由于TCP是数据流协议,因此不存在包大小的限制(暂不考虑缓冲区的大小),这是指在用send函数时,数据长度参数不受限制。而实际上,所指定的这段数据并不一定会一次性发送出去,如果这段数据比较长,会被分段发送,如果比较短,可能会等待和下一次数据一起发送。

    socketserver

    server端

    import socketserver
    class Myserver(socketserver.BaseRequestHandler):
        def handle(self):
            self.data = self.request.recv(1024).strip()
            print("{} wrote:".format(self.client_address[0]))
            print(self.data)
            self.request.sendall(self.data.upper())
    if __name__ == "__main__":
        HOST, PORT = "127.0.0.1", 9999
        # 设置allow_reuse_address允许服务器重用地址
        socketserver.TCPServer.allow_reuse_address = True
        # 创建一个server, 将服务地址绑定到127.0.0.1:9999
        server = socketserver.TCPServer((HOST, PORT),Myserver)
        # 让server永远运行下去,除非强制停止程序
        server.serve_forever()
    

    client端

    import socket
    HOST, PORT = "127.0.0.1", 9999
    data = "hello"
    # 创建一个socket链接,SOCK_STREAM代表使用TCP协议
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((HOST, PORT))          # 链接到客户端
        sock.sendall(bytes(data + "
    ", "utf-8")) # 向服务端发送数据
        received = str(sock.recv(1024), "utf-8")# 从服务端接收数据
    print("Sent:     {}".format(data))
    print("Received: {}".format(received))
    

      

  • 相关阅读:
    内核态和用户态的区别
    【Docker官方文档】理解Docker
    dpdk中kni模块
    linux的deamon后台运行
    在C语言中如何嵌入python脚本
    Docker学习之路(三)Docker网络详解
    Docker学习之路(二)DockerFile详解
    fopencookie函数详解
    Docker学习之路(一)
    C/C++代码覆盖率工具gcov、lcov
  • 原文地址:https://www.cnblogs.com/renvip/p/11322341.html
Copyright © 2011-2022 走看看