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

    一、TCP协议

    1. TCP协议的特点

        1.TCP是面向连接的运输层协议。这就意味着,在使用该协议之前,必须建立TCP连接。在传输数据完毕后,必须释放已经建立的TCP连接。
        
        2.每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的(一对一)。
        
        3.TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。
       
        4.TCP提供全双工通信。TCP允许通信双方的应用进程在任何时候都能发送数据。
            TCP连接的两端都设有发送缓存和接收缓存,用来临时存放双向通信的数据。
            在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发出去。
            在接收时,TCP把收到的数据放入缓存,上层的应用进程在合适的时候读取缓存中的数据。
        
        5.面向字节流。虽然应用程序和TCP的交互试一次一个数据块(注意:大小是不等的),
            但TCP把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。TCP并不知道所传送的字节流的含义。

    2.三次握手、四次挥手

    3.代码实现

    3.1 使用TCP协议实现简单通信
    # 客户端:
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    client.send(b"this is client!")
    data = client.recv(1024)
    print(data)
    client.close()
    
    # 服务端:
    import socket
    
    server = socket.socket()  # 实例化sever对象-->买手机
    server.bind(('127.0.0.1', 8080))  # 绑定ip和端口-->手机卡
    server.listen(5)  # 监听 -->打开手机
    conn, addr = server.accept()  # 等待建立连接
    data = conn.recv(1024)  # 接收数据 --> 打电话
    print(data)
    conn.send(b"received data!!")
    conn.close()  # 关闭连接-->挂电话
    server.close()  # 关闭服务器 -->关机
    3.2 使用TCP协议实现通信循环
    # 客户端:
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    while True:
        msg = input(">>>")
        if msg == 'q':
            break
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data)
    client.close()
    
    # 服务端
    import socket
    
    server = socket.socket()  # 实例化sever对象-->买手机
    server.bind(('127.0.0.1', 8080))  # 绑定ip和端口-->手机卡
    server.listen(5)  # 监听 -->打开手机
    conn, addr = server.accept()  # 等待建立连接
    while True:
        try:
            data = conn.recv(1024)  # 接收数据 --> 打电话
            print(data)
            conn.send(b"received data!!")
        except ConnectionResetError:
            break
    conn.close()  # 关闭连接-->挂电话
    server.close()  # 关闭服务器 -->关机
    3.3 使用TCP协议实现链接循环
    # 客户端
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    while True:
        msg = input(">>>")
        if msg == 'q':
            break
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data)
    client.close()
    
    # 服务端
    import socket
    
    server = socket.socket()  # 实例化sever对象-->买手机
    server.bind(('127.0.0.1', 8080))  # 绑定ip和端口-->手机卡
    server.listen(5)  # 监听 -->打开手机  (半连接池)
    while True:
        conn, addr = server.accept()  # 等待建立连接
        while True:
            try:
                data = conn.recv(1024)  # 接收数据 --> 打电话
                print(data.decode('utf-8'))
                conn.send(b"received data!!")
            except ConnectionResetError:
                break
        conn.close()  # 关闭连接-->挂电话
    server.close()  # 关闭服务器 -->关机
    3.4 使用TCP协议实现粘包问题
    # 客户端
    import socket
    import struct
    import json
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8090))
    while True:
        msg = input(">>>").encode('utf-8')
        if len(msg) == 0:
            continue
        client.send(msg)
        header = client.recv(4)
        # 对头进行解包,获取真实数据的长度
        head_len = struct.unpack('i', header)[0]
        head_dic = json.loads(client.recv(head_len).decode('utf-8'))
        print(head_dic)
        # 对需要接收的数据进行循环接收
        total_size = head_dic['len']
        recv_size = 0
        res = b''
        while recv_size < total_size:
            data = client.recv(1024)
            res += data
            recv_size += len(data)
        print(res.decode("gbk"))
    
    # 服务端
    import socket
    import subprocess
    import struct
    import json
    
    server = socket.socket()  # 实例化sever对象-->买手机
    server.bind(('127.0.0.1', 8090))  # 绑定ip和端口-->手机卡
    server.listen(5)  # 监听 -->打开手机  (半连接池)
    while True:
        conn, addr = server.accept()  # 等待建立连接
        while True:
            try:
                data = conn.recv(1024).decode('utf-8')  # 接收数据 --> 打电话
                if len(data) == 0: break  # 针对mac和Linux,客户端异常断开反复收空的情况
    
                # 生成一个能够读取输出、错误流的对象
                obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                stdout = obj.stdout.read()  # 获得输出流
                stderr = obj.stderr.read()  # 获取错误流
    
                # 打印输出流+输入流的长度
                print(len(stdout+stderr))
                # 制作一个文件头
                header_dic = {
                    'filename': 'cls.avi',
                    'len': len(stderr+stdout)  # 错误流 或者 输出流 的长度
                }
    
                # 序列化文件头字典,得到文件头的二进制文件
                header_bytes = json.dumps(header_dic).encode('utf-8')
    
                # 制作报头
                header = struct.pack('i', len(header_bytes))  # 将要发送给客户端的数据打包成固定4个字节
                conn.send(header)
                conn.send(header_bytes)
                conn.send(stdout+stderr)
    
            except ConnectionResetError:
                break
        conn.close()  # 关闭连接-->挂电话
    
    server.close()  # 关闭服务器 -->关机
    3.5 解决粘包问题用到的 struct
    1.说明
        struct模块中最重要的三个函数是pack(), unpack(), calcsize()
        pack(fmt, v1, v2, ...)    按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)
        unpack(fmt, string)       按照给定的格式(fmt)解析字节流string,返回解析出来的tuple
        calcsize(fmt)             计算给定的格式(fmt)占用多少字节的内存
        
        struct中支持的格式如下表:
        网址 : https://www.cnblogs.com/gala/archive/2011/09/22/2184801.html
    
    
    2.代码实现(例子)
    import struct
    data = 'datafa'
    # 服务端
    res = struct.pack('i', len(data))
    print(res)  # b'x06x00x00x00'
    print(len(res))  # 4
    3.6 解决粘包问题用到的 subprocess
    1.说明
    subprocess 模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。
    
    class subprocess.Popen(
        args, bufsize=-1, executable=None,
        stdin=None, stdout=None, stderr=None, preexec_fn=None,
        close_fds=True, shell=False, cwd=None, env=None, 
        universal_newlines=None, startupinfo=None, creationflags=0,
        restore_signals=True, start_new_session=False, 
        pass_fds=(), *, encoding=None, errors=None, text=None)
    
    
    2.代码实现(例子):
    cmd = r'dir file_list'
    obj = subprocess.Popen(
        cmd,
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    
    stdout = obj.stdout.read()  # 来接住stdout流
    print('stdout', stdout.decode('gbk'))  # 打印进程列表
    
    stderr = obj.stderr.read()
    print('stderr', stderr.decode('gbk'))
    
    # 当上面的命令出错时,接收错误流
    # stderr 'tasklist1' 不是内部或外部命令,也不是可运行的程序或批处理文件。
    # stdout 和 stderr同时存在的时候,有一个接收到了数据另一个就不会停止等待

    二、UDP协议

    1. UDP协议的特点

    1.UDP是无连接的,即发送数据之前不需要建立连接(发送数据结束时也没有连接可释放),因此减少了开销和发送数据之前的时延。
        
    2.UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表。
    
    3.UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付IP层。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
    
    4.UDP没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。
    
    5.UDP支持一对一,一对多,多对一和多对多的交互通信。
    
    6.UDP的首部开销小,只有8个字节,比TCP的20个字节的首部要短。

    2. 代码实现

    2.1 使用UDP协议实现简单通信
    # 客户端
    import socket
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    data = b'this is client'
    server_addr = ('127.0.0.1', 8080)
    
    client.sendto(data, server_addr)
    data1, addr1 = client.recvfrom(1024)
    
    print(addr1) 
    print(data1) 
    # ('127.0.0.1', 8080)
    # b'THIS IS CLIENT'
    
    # 服务端
    import socket
    
    server = socket.socket(type=socket.SOCK_DGRAM)  # udp协议
    server.bind(('127.0.0.1', 8080))
    
    while True:
        data, addr = server.recvfrom(1024)
        print(data)
        data = data.decode('utf-8')
        data = data.upper()
    
        server.sendto(data.encode('utf-8'), addr)
        
    # b'this is client'
    2.2 使用UDP协议实现极简版QQ
    # 客户端
    import socket
    
    client = socket.socket(type=socket.SOCK_DGRAM)
    server_addr = ('127.0.0.1', 8080)
    
    while True:
        data = input(">>>:").strip()
        client.sendto(data.encode('utf-8'), server_addr)
    
        msg, addr = client.recvfrom(1024)
        print(msg.decode('utf-8'))
    
    # 服务端
    import socket
    
    server = socket.socket(type=socket.SOCK_DGRAM)
    server.bind(('127.0.0.1', 8080))
    
    while True:
        data, addr = server.recvfrom(1024)
        print(addr)
        print(data.decode('utf-8'))
    
        re_msg = input(">>>:")
        server.sendto(re_msg.encode('utf-8'), addr)
    2.3 socketServer模块可以使TCP通信达到并发效果
    # 客户端
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    while True:
        msg = input(">>>:")
        client.send(msg.encode('utf-8'))
    
        data = client.recv(1024)
        print(data.decode('utf-8'))
    
    # 服务端
    import socketserver
    
    server_addr = ('127.0.0.1', 8080)
    
    class MySock(socketserver.BaseRequestHandler):
        def handle(self):
            # 通信循环
            while True:
                data = self.request.recv(1024)  # 接收数据
                print(data.decode('utf-8'))
                re_msg = input(">>>:")
                self.request.send(re_msg.encode('utf-8'))
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(server_addr, MySock)
        server.serve_forever()
  • 相关阅读:
    64位操作系统安装32位客户端和PL/SQL
    Oracle clob 操作
    Oracle中DBMS_LOB包使用小结
    PLSQL自动断开服务器连接 (转)
    oracle odbc 驱动安装(不安装oracle客户端)
    Broadcom GNSS xxx Geolocaltion Sensor与Windows导航程序的兼容性(转)
    GPS经纬度的表示方法及换算
    odp.net 读写oracle blob字段
    oracle 的数组(转)
    javascript的window.ActiveXObject对象,区别浏览器的方法
  • 原文地址:https://www.cnblogs.com/wangtenghui/p/10940535.html
Copyright © 2011-2022 走看看