python之sock套接字

时间:2021-05-05 11:00:16

scoket套接字

  1. socket

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

    ​ socket就是提供了TCP/IP协议的抽象,对外提供了一套接口,通过这个接口可以统一、方便的使用TCP/IP协议功能。

    ​ socket就是一个模块,我们通过调用模块已经实现的方法建立两个进程之间的连接和通信。

  2. 套接字分类

    • 基于文件类型的套接字家族:AF_UNIX
    • 基于网络类型的套接字家族:AF_INET
  3. 细说socket模块函数用法

    import socket
    socket.socket(socket_family,socket_type,protocal=0)
    "socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。"
    
    #获取tcp/ip套接字
    tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    #获取udp/ip套接字
    udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    #服务端套接字函数
    socket.bind()    #绑定(主机,端口号)到套接字
    socket.listen()  #开始TCP监听
    socket.accept()  #被动接受TCP客户的连接,(阻塞式)等待连接的到来
    
    #客户端套接字函数
    socket.connect()     #主动初始化TCP服务器连接
    socket.connect_ex()  #connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    
    #公共用途的套接字函数
    socket.recv()            #接收TCP数据
    socket.send()            #发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
    socket.sendall()         #发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
    socket.recvfrom()        #接收UDP数据
    socket.sendto()          #发送UDP数据
    socket.getpeername()     #连接到当前套接字的远端的地址
    socket.getsockname()     #当前套接字的地址
    socket.getsockopt()      #返回指定套接字的参数
    socket.setsockopt()      #设置指定套接字的参数
    socket.close()           #关闭套接字
    
    #面向锁的套接字方法
    socket.setblocking()     #设置套接字的阻塞与非阻塞模式
    socket.settimeout()      #设置阻塞套接字操作的超时时间
    socket.gettimeout()      #得到阻塞套接字操作的超时时间
    
    #面向文件的套接字的函数
    socket.fileno()          #套接字的文件描述符
    socket.makefile()        #创建一个与该套接字相关的文件
  4. TCP协议下的socket

    ​ 服务器端:服务器端先初始化socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。如果有客户端初始化一个socket,然后连接服务器(connect),若成功,客户端与服务端的连接就建立了。

    ​ 客户端:客户端发送数据请求,服务端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

    python之sock套接字

    • 单个客户端与服务器端通信

      服务端

      import socket
      phone = socket.socket()    # 服务端初始化
      phone.bind(('127.0.0.1',8888)) #绑定端口和ip,0——65535,1024之前系统分配好的端口
      phone.listen(5)    # 同一时刻由5个请求,但可以有n多个连接
      conn,addr = phone.accept() # 接收客户端数据发送请求
      print(conn,addr)
      from_client_data = conn.recv(1024) # 接收客户端数据,设置每次接受的最大限制bytes
      print(from_client_data.decode('utf-8'))
      conn.send(from_client_data.upper())    # 把回应数据发送给客户端
      conn.close()   # 关闭连接
      phone.close()  # 关闭服务器

      客户端

      import socket
      phone = socket.socket()    # 客户端初始化
      phone.connect(('127.0.0.1',8888)) # 与客户端建立连接
      phone.send('hello'.encode('utf-8'))    # 把数据发送给服务端
      from_server_data = phone.recv(1024)    # 接收服务端返回的数据
      print(from_server_data.decode('utf-8'))
      phone.close()  # 关闭客服端
    • 通信循环

      服务端

      import socket
      phone = socket.socket()    # 服务端初始化
      phone.bind(('127.0.0.1',8888)) #绑定端口和ip,0——65535,1024之前系统分配好的端口
      phone.listen(5)    # 同一时刻由5个请求,但可以有n多个连接
      conn,addr = phone.accept() # 接收客户端数据发送请求
      print(conn,addr)
      while 1:
          try:
              from_client_data = conn.recv(1024) # 接收客户端数据,设置每次接受的最大限制bytes
              if from_client_data == 'Q':
                  break
              print(from_client_data.decode('utf-8'))
              conn.send(input('>>>').encode('utf-8'))    # 把回应数据发送给客户端
          except ConnectionResetError:
              break
      conn.close()   # 关闭连接
      phone.close()  # 关闭服务器

      客户端

      import socket
      phone = socket.socket()    # 客户端初始化
      phone.connect(('127.0.0.1',8888)) # 与客户端建立连接
      while 1:
          content = input('>>>')
          if content.upper == 'Q':
              phone.send(content.encode('utf-8'))    # 把数据发送给服务端
              break
          phone.send(content.encode('utf-8'))   
          from_server_data = phone.recv(1024)    # 接收服务端返回的数据
          print(from_server_data.decode('utf-8'))
      phone.close()  # 关闭客服端
    • 通信,连接循环

      服务端

      import socket
      phone = socket.socket()    # 服务端初始化
      phone.bind(('127.0.0.1',8888)) #绑定端口和ip,0——65535,1024之前系统分配好的端口
      phone.listen(5)    # 同一时刻由5个请求,但可以有n多个连接
      while 1:
          print('start')
          conn,addr = phone.accept() # 接收客户端数据发送请求
          print(conn,addr)
          while 1:
              try:
                  from_client_data = conn.recv(1024) # 接收客户端数据,设置每次接受的最大限制bytes
                  if from_client_data == b'Q':
                      break
                  print(from_client_data.decode('utf-8'))
                  conn.send(input('>>>').encode('utf-8'))    # 把回应数据发送给客户端
              except ConnectionResetError:
                  break
          conn.close()   # 关闭连接
      phone.close()  # 关闭服务器

      客户端

      import socket
      phone = socket.socket()    # 客户端初始化
      phone.connect(('127.0.0.1',8888)) # 与客户端建立连接
      while 1:
          content = input('>>>').strip()
          if content.upper == 'Q':
              phone.send(content.encode('utf-8'))    # 把数据发送给服务端
              break
          phone.send(content.encode('utf-8'))   
          from_server_data = phone.recv(1024)    # 接收服务端返回的数据
          print(from_server_data.decode('utf-8'))
      phone.close()  # 关闭客服端
    • 执行远端命令

      服务端

      import socket
      phone = socket.socket()    # 服务端初始化
      phone.bind(('127.0.0.1',8888)) #绑定端口和ip,0——65535,1024之前系统分配好的端口
      phone.listen(5)    # 同一时刻由5个请求,但可以有n多个连接
      print('start')
      conn,addr = phone.accept() # 接收客户端数据发送请求
      print(conn,addr)
      while 1:
          try:
             cmd = conn.recv(1024)   # 接收客户端数据,设置每次接受的最大限制bytes
             if cmd == 'Q':
                 break
             ret = subprocess.Popen(cmd.decode('utf-8'),
                                    shell=True,
                                    stdout=subprocess.PIPE,
                                    stderr=subprocess.PIPE)
             result = ret.stdout.read()+ret.stderr.read()# 执行命令代码
             conn.send(result)   # 把回应数据发送给客户端
          except ConnectionResetError:
              break
      conn.close()   # 关闭连接
      phone.close()  # 关闭服务器

      客户端

      import socket
      phone = socket.socket()    # 客户端初始化
      phone.connect(('127.0.0.1',8888)) # 与客户端建立连接
      while 1:
          cmd = input('>>>').strip()
          if cmd.upper == 'Q':
              phone.send(content.encode('utf-8'))    # 把数据发送给服务端
              break
          phone.send(cmd.encode('utf-8'))
          ret = phone.recv(1024)
          print(ret.decode('gbk'))
      phone.close()  # 关闭客服端
    • recv工作原理

      '''
      源码解释:
      Receive up to buffersize bytes from the socket.
      接收来自socket缓冲区的字节数据,
      For the optional flags argument, see the Unix manual.
      对于这些设置的参数,可以查看Unix手册。
      When no data is available, block untilat least one byte is available or until the remote end is closed.
      当缓冲区没有数据可取时,recv会一直处于阻塞状态,直到缓冲区至少有一个字节数据可取,或者远程端关闭。
      When the remote end is closed and all data is read, return the empty string.
      关闭远程端并读取所有数据后,返回空字符串。
      '''
      ----------服务端------------:
      # 1,验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。
      
      import socket
      phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.bind(('127.0.0.1',8080))
      phone.listen(5)
      conn, client_addr = phone.accept()
      from_client_data1 = conn.recv(2)
      print(from_client_data1)
      from_client_data2 = conn.recv(2)
      print(from_client_data2)
      from_client_data3 = conn.recv(1)
      print(from_client_data3)
      conn.close()
      phone.close()
      
      # 2,验证服务端缓冲区取完了,又执行了recv执行,此时客户端20秒内不关闭的前提下,recv处于阻塞状态。
      
      import socket
      phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.bind(('127.0.0.1',8080))
      phone.listen(5)
      conn, client_addr = phone.accept()
      from_client_data = conn.recv(1024)
      print(from_client_data)
      print(111)
      conn.recv(1024) # 此时程序阻塞20秒左右,因为缓冲区的数据取完了,并且20秒内,客户端没有关闭。
      print(222)
      conn.close()
      phone.close()
      
      # 3 验证服务端缓冲区取完了,又执行了recv执行,此时客户端处于关闭状态,则recv会取到空字符串。
      
      import socket
      phone =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.bind(('127.0.0.1',8080))
      phone.listen(5)
      conn, client_addr = phone.accept()
      from_client_data1 = conn.recv(1024)
      print(from_client_data1)
      from_client_data2 = conn.recv(1024)
      print(from_client_data2)
      from_client_data3 = conn.recv(1024)
      print(from_client_data3)
      conn.close()
      phone.close()
      ------------客户端------------
      # 1,验证服务端缓冲区数据没有取完,又执行了recv执行,recv会继续取值。
      import socket
      import time
      phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.connect(('127.0.0.1',8080))
      phone.send('hello'.encode('utf-8'))
      time.sleep(20)
      phone.close()
      
      # 2,验证服务端缓冲区取完了,又执行了recv执行,此时客户端20秒内不关闭的前提下,recv处于阻塞状态。
      import socket
      import time
      phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.connect(('127.0.0.1',8080))
      phone.send('hello'.encode('utf-8'))
      time.sleep(20)
      phone.close()
      
      # 3,验证服务端缓冲区取完了,又执行了recv执行,此时客户端处于关闭状态,则recv会取到空字符串。
      import socket
      import time
      phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
      phone.connect(('127.0.0.1',8080))
      phone.send('hello'.encode('utf-8'))
      phone.close()
    1. udp协议的socket

      udp协议:不可靠,相对来说不安全的协议,面向数据报(无连接)的协议,传输效率高,速度快。

      • udp下的socket通信流程

      python之sock套接字

      服务器端先初始化Socket,然后与端口绑定(bind),recvform接收消息,这个消息有两项,消息内容和对方客户端的地址,然后回复消息时也要带着你收到的这个客户端的地址,发送回去,最后关闭连接,一次交互结束。

      • 代码如下
      # 服务端
      
      import socket
      udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 基于网络,udp协议的socket
      udp_server.bind(('127.0.0.1',8888))
      while 1:
          from_client_data = udp_server.recvfrom(1024) # 接收消息
          print(f'来自{from_client_data[1]}的消息:{from_client_data[0].decode('utf-8')}')
          to_client_data = input('>>>').strip()
          udp_server.sendto(to_client_data.encode('utf-8'),from_client_data[1]) # 发送消息
      
      
      # 客户端
      
      import socket
      udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 基于网络,udp协议的socket
      while 1:
          to_server_data = input('>>>').strip()
          udp_client.sendto(to_server_data.encode('utf-8'),('127.0.0.1',8888)) # 发送消息
          from_server_data = udp_client.recvfrom(1024) # 接收消息
          print(f'来自{from_server_data[1]}的消息:{from_server_data[0].decode('utf-8')}')