zoukankan      html  css  js  c++  java
  • socket

    socket

    • 一.前情概要
      • 1 C/S架构
      • 2 Socket
      • 3 套接字分类
    • 二.基于TCP的套接字(Socket)
      • 1 简单示例
      • 2 模拟手机打电话
      • 3 TCP粘包
        • 粘包问题解决方案
        • 基于TCP协议实现ssh功能
    • 三.基于UDP的套接字(Socket)
      • 1 简单示例

    一.前情概要

    1 C/S架构

    C/S架构就是客户端/服务器架构,
    其中server端具有以下特点:

    1. 一直提供服务
    2. 有明确的唯一的地址(ip+port),可以让客户端找到

    五层网络通信协议由上至下包括:
    应用层(数据)
    传输层(TCP/IP)
    网络层(IP)
    数据链路层(以太网)
    物理层(二进制)

    C/S架构软件是基于网络进行通信的
    Client------internet------Server

    2 Socket

    Socket就是ip+port,
    网络通信的本质就是两个应用程序间的通信,通过ip+port就可以在网络中唯一找到1个应用程序。

    Socket是应用层与传输层协议通信的中间软件抽象层,
    Socket将复杂的TCP/IP协议隐藏在Socket接口中。

    socket与TCP协议的关系:socket是对TCP协议、UDP协议...

    3 套接字分类

    套接字 :源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。
    (ps:看样子socket就是套接字)

    套接字分为两类:

    1. 基于文件类型的套接字:AF_UNIX 基于文件通信
    2. 基于网络类型的套接字:AF_INET 基于网络通信
      我们要介绍的是基于网络的套接字

    二.基于TCP的套接字(Socket)

    1 简单示例

    模拟服务端与客户端通信:

    socket server:

    import socket
    
    ip_port = ('127.0.0.1', 8989)
    
    sk = socket.socket()  # 默认TCP协议
    sk.bind(ip_port)
    sk.listen(5)
    
    while True:
        print('server waiting ...')
        conn, addr = sk.accept()
    
        client_data = conn.recv(1024)
        print(client_data)
        conn.sendall('whats up,man?')
        
        conn.close()
    
    

    socket client:

    import socket
    
    ip_port = ('127.0.0.1', 8989)
    
    sk = socket.socket()
    sk.connect(ip_port)
    
    sk.sendall(b'hello, anybody here?')
    
    server_reply = sk.recv(1024)
    print(server_reply)
    
    sk.close()
    
    

    2 模拟手机打电话

    客户端发送数据,服务器异常处理

    Socket服务端:

    import socket
    
    ip_port = ('127.0.0.1', 8191)
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # 参数1:地址簇:IPv4(默认) 参数2:流式socket , for TCP (默认)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 重用ip和端口,解决:服务重启时,会出现Address already in use错误
    phone.bind(ip_port)                                         
    phone.listen(5)                                             
    
    while True:                                                 # 循环接收连接
        print('starting...')
        conn, addr = phone.accept()
    
        while True:                                             # 循环接收消息
            try:                                                # 异常捕获方式(windows作为服务器)
                data = conn.recv(1024)
                if not data:                                    # 异常捕获方式(linux作为服务器)
                    break
                print('客户端发来的消息:', data)
                conn.send("what's going on?".encode())          # 编码为byte发送消息
            except Exception:
                break
        conn.close()
    phone.close()
    
    

    客户端:

    import socket
    
    ip_port = ('127.0.0.1', 8191)
    
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(ip_port)
    
    while True:
        msg = input('>>:').strip()
        if msg == '':
            continue
        phone.send(msg.encode('utf-8'))
    
        data = phone.recv(1024)                                 
        print(data)
    
    phone.close()
    
    

    3 TCP粘包

    问题:
    tcp是流式协议,会导致粘包问题!

    解决:
    根据自己定义协议方式解决粘包问题,自定义协议就是将传输数据增加报头

    报头要求:
    1.固定长度
    2.包含对将要发送数据的描述信息

    粘包问题解决方案

    server要做的事情:

    1.报头制作:

    # data_s = 数据
    # 1.计算数据长度 
    data_len = len(data_s)
    # 2.形成报头字典 
    head_dict = {'data_size':data_len}
    # 3.转为json字符串
    head_json = json.dumps(head_dict)  # import json
    # 4.转为字节byts
    head_byts = head_json.encode('utf-8')
    

    2.报头长度:

    # 1.报头长度:
    head_len = len(head_byts)
    # 2.处理二进制数据,按照给定的格式(fmt),把数据封装成固定长度字节
    h_l = struct.pack('i',head_len)  #import struct,这里i表示将二进制转为4字节
    

    3.数据发送:

    # 1.发送包头长度
    conn.send(h_l)
    # 2.发送包头
    conn.send(head_byts)
    # 3.发送数据
    conn.send(data)
    

    client要做的事情:

    # 1.接收报头长度
    h_l = phone.recv(4)
    head_len = struct.unpack('i',h_l)[0]  #取元组索引为0的值
    
    # 2.接收报头
    head_byts = phone.recv(head_len)
    head_json = head_byts.decode('utf-8') #解码
    head_dict = json.loads(head_json)
    data_len = head_dicr['data_size']
    
    # 3.接收数据
    recv_size = 0
    data_s = b''
    while recv_size <data_len:
        data = phone.recv(1024)
        recv_size += len(data)
        data_s += data 
        
    # 最后得到data_s
    
    基于TCP协议实现ssh功能

    server:

    import socket
    import subprocess
    import json
    import struct
    
    host_ip = '127.0.0.1'
    host_port = 8393
    ss = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ss.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    ss.bind((host_ip,host_port))
    ss.listen(5)
    while True:
        print('start listening...')
    
        conn, addr = ss.accept()
        print('客户端信息:', conn, '客户端地址:', addr)
    
        while True:
            try:
                #接收命令
                cmd = conn.recv(1024)
                if not cmd:break
                print('client的命令:',cmd)
                res = subprocess.Popen(cmd,
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
                out_res = res.stdout.read()
                err_res = res.stderr.read()
    
                data_len = len(out_res)+len(err_res)
                head_dict = {'data_size':data_len}
                head_json = json.dumps(head_dict)
                head_byts = head_json.encode('utf-8')
    
                head_len = len(head_byts)
                h_l = struct.pack('i',head_len)
    
                conn.send(h_l)
                conn.send(head_byts)
                conn.send(out_res)
                conn.send(err_res)
            except Exception:
                break
    
        conn.close()
        print('-'*60)
    ss.close()
    

    client:

    import socket
    import struct
    import json
    
    host_ip = '127.0.0.1'
    host_port = 8393
    cs = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    cs.connect((host_ip,host_port))
    
    while True:
        cmd = input('>>:').strip()
        if not cmd:continue
        cs.send(cmd.encode('utf8'))
    
        h_l = cs.recv(4)
        head_len = struct.unpack('i',h_l)[0]
    
        head_byts = cs.recv(head_len)
        head_json = head_byts.decode('utf-8')
        head_dict = json.loads(head_json)
        data_len = head_dict['data_size']
    
        recv_size = 0
        data_s = b''
        while recv_size < data_len:
            msg = cs.recv(1024)
            recv_size += len(msg)
            data_s += msg
    
        print(data_s.decode('utf8'))
    
    cs.close()
    

    三.基于UDP的套接字(Socket)

    UDP特点:
    用户数据报协议,无连接,面向消息的,自带报头(ps:发空没事,不会粘包问题)

    TCP和UDP的不同:
    tcp是可靠传输
    udp是不可靠传输

    1 简单示例

    客户端输入,服务端返回大写

    server:

    import socket
    
    ip_port = ('127.0.0.1', 8080)
    udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 数据报式socket , for UDP
    udp_server.bind(ip_port)
    
    while True:
        conn, addr = udp_server.recvfrom(1024)
        print(conn, addr)
        udp_server.sendto(conn.upper(), addr)
    
    

    client:

    import socket
    
    ip_port = ('127.0.0.1', 8080)
    udp_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 数据报式socket , for UDP
    
    while True:
        msg = input('>>:').strip()
        udp_client.sendto(msg.encode('utf-8'), (ip_port))
        conn, addr = udp_client.recvfrom(1024)
        print(conn.decode('utf-8'))
    
    
  • 相关阅读:
    bzoj 2882: 工艺 后缀自动机
    bzoj 2002: 弹飞绵羊 Link-Cut-Tree
    bzoj 3881: [Coci2015]Divljak AC自动机
    bzoj 2553: [BeiJing2011]禁忌 AC自动机+矩阵乘法
    bzoj 3172: [Tjoi2013]单词 fail树
    bzoj 2434: 阿狸的打字机 fail树+离线树状数组
    bzoj 1030: 文本生成器 AC自动机+dp
    SAS FORMAT 逻辑库存储 【输出格式 没有找到或无法加载】解决方法
    PROC UNIVARIATE 简单示例
    SAS ODS GRAPHICS SGPLOT 画图 指存放定路径、名称、指定格式
  • 原文地址:https://www.cnblogs.com/sunqim16/p/6802443.html
Copyright © 2011-2022 走看看