• socket 相关


    解决粘包问题:
      服务端:先发送报头的长度,再发送报头的内容,最后才是内容
      客户端:先先接收报头的长度,再接收报头内容(有必要的时候报头的内容都需要循环接收),然后解析报头的内容获取结果内容的信息再收取结果内容。
    服务端代码:
    # -*- coding: utf-8 -*-
    import json
    import socket
    import struct
    import subprocess
    
    '''
    Socket套接字方法
    socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    
    family(socket家族)
    socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
    socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
    
    socket type类型
    socket.SOCK_STREAM #for tcp
    socket.SOCK_DGRAM #for udp
    socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
    socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
    socket.SOCK_SEQPACKET #废弃了
    (Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
    
    proto=0 请忽略,特殊用途
    fileno=None 请忽略,特殊用途
    '''
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 如果端口已经被使用,则回收再用
    phone.bind(('localhost', 7001))  # 0-65535:0-1024给操作系统使用
    phone.listen(5)
    print('服务器已经启动。。。。。')
    while 1:  # 循环连接到服务端,不过得等前一个关掉后一个才能上
        conn, addr = phone.accept()
        print('有客户端连接上来了!')
        print(conn, addr)
        if_conn = 1
        while 1:  # 循环通信
            try:  # try except是一个客户端端断开的时候,就断开此次的服务,准备开始等待下一个
                # windows 是try的方式,linux判断if not data:break即可!
                # 1.收命令
                data = conn.recv(1024)
                # 要注意的是,客户端服务端的收发命令都是字节类型
                # print(data.decode('utf-8'))
    
                # 2.执行命令,拿执行结果
                # stdout是正确执行的结果,stderr是报错的内容
                res = subprocess.Popen(data.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                # 分别读取
                stdout = res.stdout.read()
                stderr = res.stderr.read()
    
                # 3、把命令的结果返回给客户端
                # 第一步:制作固定长度的报头
                header_dic = {
                    'filename': 'a.txt',
                    'md5': 'xxdxxx',
                    'total_size': len(stdout) + len(stderr)
                }
    
                header_json = json.dumps(header_dic)
    
                header_bytes = header_json.encode('utf-8')
    
                # 第二步:先发送报头的长度
                conn.send(struct.pack('i', len(header_bytes)))
    
                # 第三步:再发报头
                conn.send(header_bytes)
    
                # 第四步:再发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:  # 适用于windows操作系统
                break
        conn.close()
    
    phone.close()
    View Code

      客户端

    # -*- coding: utf-8 -*-
    import json
    import socket
    import struct
    
    '''
    Socket套接字方法
    socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    
    family(socket家族)
    socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
    socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
    
    socket type类型
    socket.SOCK_STREAM #for tcp
    socket.SOCK_DGRAM #for udp
    socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
    socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
    socket.SOCK_SEQPACKET #废弃了
    (Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
    
    proto=0 请忽略,特殊用途
    fileno=None 请忽略,特殊用途
    '''
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    phone.connect(('127.0.0.1', 7001))
    
    while True:
        # 1、发命令
        cmd = input('>>: ').strip()  # ls /etc
        if cmd:
            phone.send(cmd.encode('utf-8'))
    
            # 2、拿命令的结果,并打印
    
            # 第一步:先收报头的长度
            obj = phone.recv(4)
            header_size = struct.unpack('i', obj)[0]
    
            # 第二步:再收报头
            header_bytes = phone.recv(header_size)
    
            # 第三步:从报头中解析出对真实数据的描述信息
            header_json = header_bytes.decode('utf-8')
            header_dic = json.loads(header_json)
            print(header_dic)
            total_size = header_dic['total_size']
    
            # 第四步:接收真实的数据
            recv_size = 0
            recv_data = b''
            while recv_size < total_size:
                res = phone.recv(1024)  # 1024是一个坑,但这里已经解决啦
                recv_data += res
                recv_size += len(res)
            print(recv_data.decode('gbk'))  # 这里是gbk,因为是这里是在windows上运行的,windows的默认编码是gbk,其他操作系统的要注意
    
    phone.close()
    View Code


    简单多次通信
    server ,并且当客户端断开的时候,服务端自动等待下一次链接,而不是报错(windows下的情况,windows是try except 来捕获异常,linux下判断收到的是否为空即可,这里是windows的情况)(windows是报错,linux是死循环)。
    # -*- coding: utf-8 -*-
    import socket
    
    '''
    Socket套接字方法
    socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    
    family(socket家族)
    socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
    socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
    
    socket type类型
    socket.SOCK_STREAM #for tcp
    socket.SOCK_DGRAM #for udp
    socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
    socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
    socket.SOCK_SEQPACKET #废弃了
    (Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
    
    proto=0 请忽略,特殊用途
    fileno=None 请忽略,特殊用途
    '''
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 如果端口已经被使用,则回收再用
    phone.bind(('localhost', 7001))  # 0-65535:0-1024给操作系统使用
    phone.listen(5)
    print('start')
    while 1:
        conn, addr = phone.accept()
        print('有客户端连接上来了!')
        print(conn, addr)
        if_conn = 1
        while 1:
            try:
                data = conn.recv(1024)
                print(data.decode('utf-8'))
                conn.send(data.decode('utf-8').upper().encode('utf-8'))
            except ConnectionResetError:
                break
    conn.close()
    phone.close()
    View Code

     client

    # -*- coding: utf-8 -*-
    import socket
    
    '''
    Socket套接字方法
    socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    
    family(socket家族)
    socket.AF_UNIX:用于本机进程间通讯,为了保证程序安全,两个独立的程序(进程)间是不能互相访问彼此的内存的,但为了实现进程间的通讯,可以通过创建一个本地的socket来完成
    socket.AF_INET:(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)
    
    socket type类型
    socket.SOCK_STREAM #for tcp
    socket.SOCK_DGRAM #for udp
    socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
    socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
    socket.SOCK_SEQPACKET #废弃了
    (Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)
    
    proto=0 请忽略,特殊用途
    fileno=None 请忽略,特殊用途
    '''
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    phone.connect(('localhost', 7001))
    while 1:
        cmd = input('>>:').strip()
        if cmd:
            phone.send(cmd.encode('utf-8'))
            get = phone.recv(1024)
            print(get.decode('utf-8'))
    
    phone.close()
    View Code


    老男孩视频整理根据文件大小判断接收文件
    #client同时进行了md5加密
    import socket
    import hashlib
    
    client = socket.socket()
    client.connect(('localhost',9998))
    while True:
        cmd = input("请输入:").strip()
        if len(cmd)==0:continue
        if cmd.startswith('get'):
            client.send(cmd.encode())
            server_response = client.recv(1024)
            print("server response:",server_response)
            client.send(b'ready to recv file')
            file_total_size = int(server_response.decode())
            received_size = 0
            filename = cmd.split()[1]
            f = open(filename+'new','wb')
            m = hashlib.md5()
    
            while received_size < file_total_size:
                if file_total_size - received_size >1024:#收不止一次
                    size = 1024
                else:#最后一次收了,剩多少收多少,不能多收
                    size = file_total_size - received_size
                    print('last receive:',size)
    
                data = client.recv(size)
                received_size += len(data)
                m.update(data)
                f.write(data)
            else:
                new_file_md5 = m.hexdigest()
                print('file receive done ',received_size,file_total_size)
                f.close()
            server_file_md5 = client.recv(1024)
            print('server file md5:',server_file_md5)
            print('client file md5',new_file_md5)
    
    client.close()
    View Code
    #server
    import socket,hashlib,os,time
    server = socket.socket()
    server.bind(('localhost',9998))
    server.listen()
    
    
    while True:
        conn,addr = server.accept()
        print('等待新指令:')
        data = conn.recv(1024)
        if not data:
            print('客户端已断开')
            break
        cmd,filename = data.decode().split()
        print(filename)
        if os.path.isfile(filename):
    
            f = open(filename,'rb')
            m = hashlib.md5()
            file_size = os.stat(filename).st_size
            conn.send(str(file_size).encode())
            conn.recv(1024)
            for line in f :
                m.update(line)
                conn.send(line)
            print('file md5',m.hexdigest())
            f.close()
            conn.send(m.hexdigest().encode()) #send md5
        print('send done')
    
    server.close()
    View Code
  • 相关阅读:
    Go基础系列:流程控制结构
    Go基础系列:数据类型转换(strconv包)
    Go基础系列:简单数据类型
    Go基础系列:常量和变量
    Go基础系列:map类型
    Go基础系列:Go slice详解
    go基础系列:数组
    Go基础系列:import导包和初始化阶段
    Go基础系列:构建go程序
    go基础系列:结构struct
  • 原文地址:https://www.cnblogs.com/Simonsun002/p/8168650.html
Copyright © 2020-2023  润新知