英语名词

wordsexplain
backlog积压的待办工作
imperative必要的, 不可避免的
it's imperative to do sth.
switchboard配电盘
switchboard operator总机,接线员(电话接线员)
overhead经常性的
insideout心中有数(我自己认为的解释)
your app should be almost insideout.
multiplex多路的,多工的
simplex单一的,单工的

教程

https://realpython.com/python-sockets/#background 英文,易懂全面

https://docs.python.org/3/library/socketserver.html socketserver 模块

https://docs.python.org/3/library/socket.html 官方 socket 参考

https://docs.python.org/3/howto/sockets.html#socket-howto python 官方 socket 原理解说

  • linux 平台

    • man socket

      • 关于 socket 的 C 语言接口说明

概念

  • 参考

  • MSL 和 TTL

    MSL

    Maximum Segment Lifetime

    • 报文最大生存时间,报文在网络上生存的最大时间
    TTL

    Time to live

    • 生存时间,报文经过的最大路由数量

mysocket.bind((host, port))

  • 参数

    • host 取值

      • "127.0.0.1", loopback, 只有本机能够连接
      • "localhost", 同 127.0.0.1 效果一致
      • 空串"",绑定到本机所有 ip 地址
    • port 取值

      • 0, 保留值,不能使用
      • 1~65535,可以使用

mysocket.socket()

参数

  1. address family 地址

    • ip 协议

      • ipv4, socket.AF_INET
      • ipv6, socket.AF_INET6
      • AF —> address family
      • 本地通讯, socket.AF_UNIX, socket.AF_LOCAL
  2. type

    • 流类型
    • socket.SOCK_STREAM
  3. protocol

    • 协议类型

      • TCP
      • UDP

mysocket.listen()

允许积压 backlog 连接申请数量

  • 无参,使用默认值
  • Linux

    • 系统允许的最多连接数量
    • 通过文件配置

      • /proc/sys/net/core/somaxconn

属性获取

socket.gethostname()

模块 socket 的函数,获取本机的 hostname

mysocket.getsockname()

  • 返回值

    • (ip, port)

传输数据

方式

  1. recv() 和 send()
  2. write() 和 read()

    • 实现方法

      • 把 socket 转换城 file-like 对象
    • 注意

      • file-like 存在缓存 buffer 问题
      • 写完数据,需要调用 flush()
      • 不然对方,永远收不到
      • 因为,数据还存在自己的 outpu buffer 中

mysocket.recv() 和 mysocket.send()

结束标志

  1. socket 本身并没有结束标志
  2. 单次使用的 socket

    • 当对方调用了 close(), 已经不关闭或正在关闭
    • 这时 recv(), 获取的是零个 byte,即(b'')
    • 可以把它当作结束
    • 实例

      • http 协议,一般都是每一次传输创建一个 socket
  3. 重复使用

    • 三种方法

      • 消息定长
      • 消息有确定的“结束标志”
      • 关闭连接

        • 消息处理方式
      • 前缀法

        • 在消息的前部,设置类型或长度标志

          • 缺点

            • 对方原样返回,会造成错误

mysocket.sendall()

  • 一次性传输所有数据
  • 不同于 mysocket.send(): 单次只能传输一定数据,循环调用

mysocket.close() 关闭与 disconnecting

  • shutdown()

    • 位置:应当在 close()前调用 shutdown()
    • 作用:表明发送数据 send()结束,但是 recv()还在进行

确保关闭

  • 需要调用 close()函数
  • 可以使用 with

    1
    2
    
    with socket.socket() as s:
        pass
  • 使用 try, finnaly

    1
    2
    3
    4
    5
    
    try:
        conn, addr = server.accept()
        conn.recv(10)
    finally:
        conn.close()

异常中断

  • 工具

    • 返回值判断

      • mysocket.send() == 0
      • mysocket.recv() == 0

         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        
        def myreceive(self):
            chunks = []
            bytes_recd = 0
            while bytes_recd < MSGLEN:
            chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048))
            if chunk == b'':
                raise RuntimeError("socket connection broken")
            chunks.append(chunk)
            bytes_recd = bytes_recd + len(chunk)
            return b''.join(chunks)
  • 原因

    • 另一方在未调用 close()之前断开连接
  • 处理

    • 如果是线程,可以不必杀掉线程

非阻塞型 socket Non-Blocking Socket

https://docs.python.org/3/howto/sockets.html#non-blocking-sockets

  • 方法

    1. socket.setblocking(0)

      • 程序可能会很复杂
    2. 使用 select 模块

      • select 有一个对应的高级模块 slectors

makefile() 转换 file-like object

mysocket.makefile()

获取未被占用端口

  1. 申请端口
  2. 设置端口可重用 sock.SO_REUSEADDR
  3. 提取端口号
  4. 关闭 socket
1
2
3
4
5
6
7
8
import socket
from contextlib import closing

def find_free_port():
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
    s.bind(('', 0))
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    return s.getsockname()[1]