zoukankan      html  css  js  c++  java
  • socket套接字

    一、用套接字实现简单通信

    1.1服务端

    import socket
    
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP地址和端口号
    soc.bind(('192.168.11.195',8080))
    #处于监听状态
    soc.listen(5)
    #准备接收数据
    conn,addr = soc.accept()
    #接收数据,最大为1024字节
    data= conn.recv(1024)
    # 打印客户端发来的信息
    print('客户端发来的数据:',data)
    #发送信息
    conn.send(b'sjdiuamjnd')
    # 关闭通信
    conn.close()
    #关闭连接
    soc.close()

    1.2 客户端

    import socket
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    soc.send(b'adndhbv')
    #接收1024个字节
    data = soc.recv(1024)
    #打印接收的数据
    print('服务端接受的数据:',data)
    #关闭连接
    soc.close()

    二、用套接字实现通信循环


    由于简单通信,客户端和服务器发一次数据就端来连接了,我们现在要用一种方法让他实现,客户端一直向服务器发消息,并且在服务器上显示内容而连接不断开。

    2.1服务端

    import socket
    
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP地址和端口号
    soc.bind(('192.168.11.195',8080))
    #处于监听状态
    soc.listen(5)
    #准备接收数据
    conn,addr = soc.accept()
    #接收数据,最大为1024字节
    while True:
        try:
            data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
            # 打印客户端发来的信息
            print('客户端发来的数据:',data)
        except Exception:  #异常处理
            break
            conn.close()        # 关闭通信
    #关闭连接
    soc.close()

    2.2客户端

    import socket
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    while True:  #加入循环
        data = input('请输入你要发送的数据>>>')
        data= bytes(data,encoding='utf8')  #将数据转化为bytes格式
        soc.send(data)
    #关闭连接
    soc.close()

    三、用套接字实现连接循环


    由于通信循环也只是一个客户端和一个服务器进行交互,如果有其他的客户端想要连接这个服务端,就连不进去,我们怎样去实现一台服务器可以和不仅是一个客户端建立连接呢?我们就需要连接循环,只要一个客户端和服务器断了,服务器还可以等待其他客户端来连接它。
    ### 3.1服务端

    import socket
    
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP地址和端口号
    soc.bind(('192.168.11.195',8080))
    #处于监听状态
    soc.listen(5)   #半连接池的大小
    #准备接收数据
    while True:  #加入循环
        print('等待客户端连接>>>>')
        conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
        print(f'客户端{addr}连接成功')
        #接收数据,最大为1024字节
        while True:
            try:
                data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
                # 打印客户端发来的信息
                print('客户端发来的数据:',data)
                # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
                if len(data) == 0:
                    break
            except Exception:  #异常处理
                break
                conn.close()  # 关闭通信
    #关闭连接
    soc.close()
    -------------------------------------------------------------------------------------
    等待客户端连接>>>>
    客户端('192.168.11.195', 51460)连接成功
    客户端发来的数据: b'xcv'
    客户端发来的数据: b'c'
    等待客户端连接>>>>
    客户端('192.168.11.195', 51467)连接成功
    客户端发来的数据: b'zxcv'

    ### 3.2 客户端

    import socket
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    while True:  #加入循环
        data = input('请输入你要发送的数据>>>')
        data= bytes(data,encoding='utf8')  #将数据转化为bytes格式
        soc.send(data)
    #关闭连接
    soc.close()

    四、模拟SSH(远程执行命令)功能


    4.1 subprocess模块

    • 什么是subprocess模块?

      执行系统命令的模块

    import subprocess  #导入模块
    
    #执行系统dir命令,把执行的正确结果放到stdout管道中,把错误的命令放在stderr管道中(dir:查看当前文件夹下的所有文件,tasklist:查看当前正在运行的系统任务)
    obj=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    #拿到正确结果的管道,读出里面的内容
    yes = obj.stdout.read()
    no = obj.stdeer.read()
    #windows的命令是gbk编码,所以用gbk解码
    print('错误信息',str(yes,encoding= 'gbk'))   
    print('错误信息',str(no,encoding= 'gbk'))   

    4.2服务端

    import socket
    import subprocess
    
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP地址和端口号
    soc.bind(('192.168.11.195',8080))
    #处于监听状态
    soc.listen(5)
    #准备接收数据
    while True:  #加入循环
        print('等待客户端连接>>>>')
        conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
        print(f'客户端{addr}连接成功')
        #接收数据,最大为1024字节
        while True:
            try:
                data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
                # 打印客户端发来的信息
                print('客户端发来的数据:',data)
                # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
                if len(data) == 0:
                    break
                print(data)
                obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
                msg = obj.stdout.read()   #将数据读出来
                conn.send(msg)   #将读取的数据通过网络发送给c端
                print(111)
            except Exception:  #异常处理
                break
                conn.close()  # 关闭通信
    #关闭连接
    soc.close()
    

    4.3客户端

    import socket
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    while True:  #加入循环
        data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
        data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
        soc.send(data_inp)  #将转化好的数据发送到服务端
        data_rec = soc.recv(1024)  #接收来自服务端的数据数据
        print(str(data_rec,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码
    #关闭连接
    soc.close()

    五、粘包问题


    我们先来看看一个粘包的例子

    服务器
    ---------------------------------------------------------------------------
    import socket
    
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP地址和端口号
    soc.bind(('192.168.11.195',8080))
    #处于监听状态
    soc.listen(5)
    #准备接收数据
    while True:  #加入循环
        print('等待客户端连接>>>>')
        conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
        print(f'客户端{addr}连接成功')
        #接收数据,最大为1024字节
        while True:
            try:
                data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
                # 打印客户端发来的信息
                print('客户端发来的数据:',data)
                # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
                if len(data) == 0:
                    break
            except Exception:  #异常处理
                break
                conn.close()  # 关闭通信
    #关闭连接
    soc.close()
    -----------------------------------------------------------------
    等待客户端连接>>>>
    客户端('192.168.11.195', 51569)连接成功
    客户端发来的数据: b'aaa'
    客户端发来的数据: b'bbbccc'
    客户端
    ---------------------------------------------------------------------------------------
    import socket
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    while True:  #加入循环
        data = input('请输入你要发送的数据>>>')
        data= bytes(data,encoding='utf8')  #将数据转化为bytes格式
        soc.send(b'aaa')
        soc.send(b'bbb')
        soc.send(b'ccc')
    #关闭连接
    soc.close()

    5.1 发生粘包的两种情况

    • 发送端需要等缓冲区满才发送出去,造成粘包(发送时间间隔很短,数据又很小,会合到一起,产生粘包)
    • 接收端不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次手的时候还是从缓冲区呐上次一流的数据,产生粘包)

    5.2 解决粘包的处理办法

    1)发送端在发送数据前让接收端知道字节流(数据)的大小,为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。

    服务器
    -----------------------------------------------------------------------------------------
    import socket
    import subprocess
    import struct
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP地址和端口号
    soc.bind(('192.168.11.195',8080))
    #处于监听状态
    soc.listen(5)
    #准备接收数据
    while True:  #加入循环
        print('等待客户端连接>>>>')
        conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
        print(f'客户端{addr}连接成功')
        #接收数据,最大为1024字节
        while True:
            try:
                data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
                # 打印客户端发来的信息
                print('客户端发来的数据:',data)
                # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
                if len(data) == 0:
                    break
                    print(data)
                obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
                msg = obj.stdout.read()   #将数据读出来
    
                # 先取出要发送数据长度length     #接下来是给客户端数据
                length = len(msg)
                # head 是固定四个字节
                data_head = struct.pack('i', length)   #将数据的长度的信息打包压缩成4个字节,作为数据头,先发过去
                # 发了数据头
                conn.send(data_head)
                # 发送真正的内容
                conn.send(msg)   #将读取的数据通过网络发送给c端
            except Exception:  #异常处理
                break
        conn.close()  # 关闭通信
    #关闭连接
    soc.close()
    客户端
    -------------------------------------------------------------------------------------
    import socket
    import struct  ##把一个数字打包成固定长度的4字节
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    while True:  #加入循环
        data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
        data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
        soc.send(data_inp)  #将转化好的数据发送到服务端
        #接下来是收服务器发来的数据
        data_head = soc.recv(4)        #先收四个字节,作为数据头部
    
        # 对数据头解压缩,得到数据的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
        length= struct.unpack('i',data_head)[0]
    
        count = 0
        data_total = b''   #数据先定义为空
        while count<length:
            if length<1024:  #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
                data = soc.recv(length)
                break
            else:#如果接收的数据小于1024
                if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024  ,再收1024
                    data= soc.recv(1024)
                else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
                    data= soc.recv(length-count)
    
            data_total += data
            count +=len(data)
        print(str(data_total,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码
    
    
    #关闭连接
    soc.close()import socket
    import struct  ##把一个数字打包成固定长度的4字节
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    while True:  #加入循环
        data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
        data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
        soc.send(data_inp)  #将转化好的数据发送到服务端
        #接下来是收服务器发来的数据
        data_head = soc.recv(4)        #先收四个字节,作为数据头部
    
        # 对数据头解压缩,得到数据的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
        length= struct.unpack('i',data_head)[0]
    
        count = 0
        data_total = b''   #数据先定义为空
        while count<length:
            if length<1024:  #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
                data = soc.recv(length)
                break
            else:#如果接收的数据小于1024
                if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024  ,再收1024
                    data= soc.recv(1024)
                else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
                    data= soc.recv(length-count)
    
            data_total += data
            count +=len(data)
        print(str(data_total,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码
    
    
    #关闭连接
    soc.close()

    2)我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个字节足够用了)

    • 发送时:

      先发报头长度

      再编码报头内容然后发送

      最后发真实内容

    • 接收时:

      先手报头长度,用struct取出来

      根据取出的长度收取报头内容,然后解码,反序列化

      从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

    服务端
    --------------------------------------------------------------------------------
    import socket
    import subprocess
    import struct
    import json
    soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #绑定IP地址和端口号
    soc.bind(('192.168.11.195',8080))
    #处于监听状态
    soc.listen(5)
    #准备接收数据
    while True:  #加入循环
        print('等待客户端连接>>>>')
        conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
        print(f'客户端{addr}连接成功')
        #接收数据,最大为1024字节
        while True:
            try:
                data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
                # 打印客户端发来的信息
                print('客户端发来的数据:',data)
                # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
                if len(data) == 0:
                    break
                    print(data)
                obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
                msg = obj.stdout.read()   #将数据读出来
    
                # 先取出要发送数据长度length     #接下来是给客户端传数据)
                dic= {'size':len(msg)}   #定义一个字典,把数据长度放到字典里面
                dic_bytes = (json.dumps(dic).encode('utf8'))   #将字典序列化,转为bytes格式
                # head 字典的长度是固定四个字节的长度
                data_head = struct.pack('i', len(dic_bytes))   #将数据字典的长度的信息打包压缩成4个字节,作为数据头,先发过去
                # print(data_head)   #b'x0fx00x00x00'
                # 发送数据头
                conn.send(data_head)   #发送4个字节的长度
                # 发送真正的数据头部
                conn.send(dic_bytes)
                #发送真正的数据内容
                conn.send(msg)   #将读取的数据通过网络发送给c端
    
            except Exception:  #异常处理
                break
        conn.close()  # 关闭通信
    #关闭连接
    soc.close()
    客户端
    -------------------------------------------------------------------------------------
    import socket
    import struct  ##把一个数字打包成固定长度的4字节
    import json
    
    soc = socket.socket()
    #连接IP地址和端口号
    soc.connect(('192.168.11.195',8080))
    #发送消息
    while True:  #加入循环
        data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
        data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
        soc.send(data_inp)  #将转化好的数据发送到服务端(接下来其实就是把这一句做一个改造,让他更牛x)
        #接下来是收服务器发来的数据
        head_dic_len = soc.recv(4)        #先收四个字节,这四个字节是头部字典的长度
        # 对头部字典解压缩,得到字典的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
        dic_len= struct.unpack('i',head_dic_len)[0]
        dic_byte = soc.recv(dic_len)  # byte 字典的长度,#收真正的头部字典
        data_head = json.loads(dic_byte)  #反序列化
        print(data_head)    #是一个字典{'size': 17396}
        length = data_head['size']
    
        count = 0
        data_total = b''   #数据先定义为空
        print(length)    #17396
        while count<length:
            if length<1024:  #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
                data = soc.recv(length)
                break
            else:#如果接收的数据小于1024
                if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024  ,再收1024
                    data= soc.recv(1024)
                else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
                    data= soc.recv(length-count)
    
            data_total += data
            count +=len(data)
        print(str(data_total,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码
    
    #关闭连接
    soc.close()
     
  • 相关阅读:
    Applet
    Tutorial中代码的区别及不同效果
    Session
    代码解析&Filter用户授权例子
    Web的后台运作过程
    XML
    数据库
    spring常用注解
    spring mvc 常用注解
    @requestMapping
  • 原文地址:https://www.cnblogs.com/lulingjie/p/11529039.html
Copyright © 2011-2022 走看看