zoukankan      html  css  js  c++  java
  • socket编程

    一.socket介绍

    1.socket在tcp/ip五层结构中的位置

      socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

    2.类型

      基于文件类型的套接字家族

      套接字家族的名字:AF_UNIX

      unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

      基于网络类型的套接字家族

      套接字家族的名字:AF_INET

      (还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET)

    3.套接字工作流程

    服务端套接字函数
    s.bind()    绑定(主机,端口号)到套接字
    s.listen()  开始TCP监听
    s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来
    
    客户端套接字函数
    s.connect()     主动初始化TCP服务器连接
    s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    
    公共用途的套接字函数
    s.recv()            接收TCP数据
    s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    s.recvfrom()        接收UDP数据
    s.sendto()          发送UDP数据
    s.getpeername()     连接到当前套接字的远端的地址
    s.getsockname()     当前套接字的地址
    s.getsockopt()      返回指定套接字的参数
    s.setsockopt()      设置指定套接字的参数
    s.close()           关闭套接字
    
    面向锁的套接字方法
    s.setblocking()     设置套接字的阻塞与非阻塞模式
    s.settimeout()      设置阻塞套接字操作的超时时间
    s.gettimeout()      得到阻塞套接字操作的超时时间
    
    面向文件的套接字的函数
    s.fileno()          套接字的文件描述符
    s.makefile()        创建一个与该套接字相关的文件
    套接字方法大全

    二.基于TCP的套接字

    1.以打电话为例实现套接字通信

    import socket
    
    #买手机
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    #绑定手机卡
    phone.bind(('127.0.0.1',8080))
    #开机
    phone.listen(5)            #最大挂起的连接数
    #等待连接
    print('starting...')
    conn,client_addr=phone.accept()       #(套接字链接,客户端的ip和port)
    print('>>>>>>>')
    #收消息
    data=conn.recv(1024)            #1024最大的限制
    print('客户端消息:',data)
    #发消息
    conn.send(data.upper())
    #挂电话
    conn.close()
    #关机
    phone.close()
    服务端
    import socket
    
    #买手机
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    #发起连接
    phone.connect(('127.0.0.1',8080))
    #发消息
    phone.send('hello world'.encode('utf-8'))
    #收消息
    data=phone.recv(1024)
    print(data)
    #关机
    phone.close()
    客户端

    2.加上链接循环与通信循环

    import socket
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)            #最大挂起的连接数
    while True:
        conn,client_addr=phone.accept()       #(套接字链接,客户端的ip和port)
        print(client_addr)
        while True: #通信循环
            try:
                data=conn.recv(1024)            #1024最大的限制
                print('客户端消息:',data)
                if not data:break   #针对Linux系统
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
    phone.close()
    服务端
    import socket
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.connect(('127.0.0.1',8080))
    while True:
        msg=input('>>:').strip()
        if not msg: continue
        phone.send(msg.encode('utf-8'))
        data=phone.recv(1024)
        print(data.decode('utf-8'))
    
    phone.close()
    客户端

    3.实现ssh远程执行命令

    import socket,subprocess
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)            #最大挂起的连接数
    while True:
        conn,client_addr=phone.accept()       #(套接字链接,客户端的ip和port)
        print(client_addr)
        while True: #通信循环
            try:
                cmd=conn.recv(1024)            #1024最大的限制
                print('客户端消息:',cmd)
                if not cmd:break   #针对Linux系统
                obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
                stdout_res=obj.stdout.read()
                stderr_res=obj.stderr.read()
                conn.send(stdout_res+stderr_res)
            except ConnectionResetError:
                break
        conn.close()
    phone.close()
    服务端
    import socket
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.connect(('127.0.0.1',8080))
    while True:
        cmd=input('>>:').strip()
        if not cmd: continue
        phone.send(cmd.encode('utf-8'))
        cmd_res=phone.recv(1024)
        print(cmd_res.decode('gbk'))
    phone.close()
    客户端

    三.粘包

     处理粘包1(加上固定长度的报头):

    import socket,subprocess,struct
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)            #最大挂起的连接数
    while True:
        conn,client_addr=phone.accept()       #(套接字链接,客户端的ip和port)
        print(client_addr)
        while True: #通信循环
            try:
                cmd=conn.recv(1024)            #1024最大的限制
                print('客户端消息:',cmd)
                if not cmd:break   #针对Linux系统
                obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
                stdout_res=obj.stdout.read()
                stderr_res=obj.stderr.read()
                #先发报头
                data_size=len(stdout_res)+len(stderr_res)
                conn.send(struct.pack('i',data_size))
                conn.send(stdout_res)
                conn.send(stderr_res)
            except ConnectionResetError:
                break
        conn.close()
    phone.close()
    服务端
    import socket,struct
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.connect(('127.0.0.1',8080))
    while True:
        cmd=input('>>:').strip()
        if not cmd: continue
        phone.send(cmd.encode('utf-8'))
        #先收报头
        head_strct=phone.recv(4)
        total_size=struct.unpack('i',head_strct)[0]
        cmd_res=b''
        recv_size=0
        while recv_size<total_size:
            recv_data=phone.recv(1024)
            cmd_res+=recv_data
            recv_size+=len(recv_data)
        print(cmd_res.decode('gbk'))
    phone.close()
    客户端

     处理粘包2(加上反映数据信息的报头,和报头长度):

    import socket,subprocess,struct,json
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)            #最大挂起的连接数
    while True:
        conn,client_addr=phone.accept()       #(套接字链接,客户端的ip和port)
        print(client_addr)
        while True: #通信循环
            try:
                cmd=conn.recv(1024)            #1024最大的限制
                print('客户端消息:',cmd)
                if not cmd:break   #针对Linux系统
                obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE)
                stdout_res=obj.stdout.read()
                stderr_res=obj.stderr.read()
                #制作报头
                head_dic={
                    'filename':'a.txt',
                    'total_size':len(stdout_res)+len(stderr_res),
                    'md5':'fiwehfoaejrfanf'
                }
                head_json=json.dumps(head_dic).encode('utf-8')
                #先发报头长度
                conn.send(struct.pack('i',len(head_json)))
                #再发报头
                conn.send(head_json)
                #再发数据
                conn.send(stdout_res)
                conn.send(stderr_res)
            except ConnectionResetError:
                break
        conn.close()
    phone.close()
    服务端
    import socket,struct,json
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #tcp协议
    phone.connect(('127.0.0.1',8080))
    while True:
        cmd=input('>>:').strip()
        if not cmd: continue
        phone.send(cmd.encode('utf-8'))
        #先收报头长度
        struct_res=phone.recv(4)
        head_size=struct.unpack('i',struct_res)[0]
        #先收报头
        head_json=phone.recv(head_size).decode('utf-8')
        head_dic=json.loads(head_json)
    
        cmd_res=b''
        recv_size=0
        total_size=head_dic['total_size']
        while recv_size<total_size:
            recv_data=phone.recv(1024)
            cmd_res+=recv_data
            recv_size+=len(recv_data)
        print(cmd_res.decode('gbk'))
    phone.close()
    客户端

    四. 基于UDP的套接字

    #服务器端
    import socket
    ip_port=('127.0.0.1',9000)
    BUFSIZE=1024
    #创建一个服务器的套接字
    udp_server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    #绑定服务器套接字
    udp_server.bind(ip_port)
    
    while True:
        #接收
        msg,addr=udp_server.recvfrom(BUFSIZE)
        print(msg,addr)
        #发送
        udp_server.sendto(msg.upper(),addr)
    
    udp_server.close()
    import socket
    ip_port=('127.0.0.1',9000)
    BUFSIZE=1024
    # 创建客户套接字
    udp_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    while True:
        msg=input('>>: ').strip()
        if not msg:continue
       #发送
        udp_client.sendto(msg.encode('utf-8'),ip_port)
        #接收
        back_msg,addr=udp_client.recvfrom(BUFSIZE)
        print(back_msg.decode('utf-8'),addr)
  • 相关阅读:
    SQLServerframework启动报异常:Module的类型初始值设定项引发异常
    在coding或者github建立个人站点域名绑定
    Github速度慢的解决方法
    jsoup爬取网站图片
    activeMQ类别和流程
    Session session = connection.createSession(paramA,paramB);参数解析
    Ehcache入门经典:第二篇ehcache.xml的参数
    Ehcache入门经典:第一篇
    处理高并发
    扩充次数和创建个数问题
  • 原文地址:https://www.cnblogs.com/hantaozi430/p/7800064.html
Copyright © 2011-2022 走看看