Python KCP协议实例

Python KCP协议实例

KCP协议介绍

  • 官网介绍KCP是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,且最大延迟降低三倍的传输效果。纯算法实现,并不负责底层协议(如UDP)的收发,需要使用者自己定义下层数据包的发送方式,以 callback的方式提供给 KCP。 连时钟都需要外部传递进来,内部不会有任何一次系统调用。
  • kcp协议是传输层的一个具有可靠性的传输层ARQ协议。它的设计是为了解决在网络拥堵情况下tcp协议的网络速度慢的问题。kcp力求在保证可靠性的情况下提高传输速度。kcp协议的关注点主要在控制数据的可靠性和提高传输速度上面,因此kcp没有规定下层传输协议,一般用udp作为下层传输协议,kcp层协议的数据包在udp数据报文的基础上增加控制头。

KCP协议使用流程

  • 1、创建 KCP对象:

    1
    2
    3
    // 初始化 kcp对象,conv为一个表示会话编号的整数,和tcp的 conv一样,通信双
    // 方需保证 conv相同,相互的数据包才能够被认可,user是一个给回调函数的指针
    ikcpcb *kcp = ikcp_create(conv, user);
  • 2、设置回调函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // KCP的下层协议输出函数,KCP需要发送数据时会调用它
    // buf/len 表示缓存和长度
    // user指针为 kcp对象创建时传入的值,用于区别多个 KCP对象
    int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)
    {
    ....
    }
    // 设置回调函数
    kcp->output = udp_output;
  • 3、循环调用 update:

    1
    2
    3
    // 以一定频率调用 ikcp_update来更新 kcp状态,并且传入当前时钟(毫秒单位)
    // 如 10ms调用一次,或用 ikcp_check确定下次调用 update的时间不必每次调用
    ikcp_update(kcp, millisec);
  • 4、输入一个下层数据包:

    1
    2
    3
    // 收到一个下层数据包(比如UDP包)时需要调用:
    ikcp_input(kcp, received_udp_packet, received_udp_size);
    处理了下层协议的输出/输入后 KCP协议就可以正常工作了,使用 ikcp_send 来向 远端发送数据。而另一端使用 ikcp_recv(kcp, ptr, size)来接收数据。
  • kcp源码流程图:
    1.png

  • 接收数据包流程:接收数据包时,UDP自带的recv函数收到的包,不断通过kcp_input喂给KCP,KCP会对这部分数据(KCP协议数据)进行解包,重新封装成应用层用户数据,应用层通过kcp_recv获取。
  • 发送数据包流程:发送数据包时,应用层通过kcp_send发送数据,KCP会把用户数据拆分kcp数据包,通过kcp_output,以UDP(send)的方式发送。
  • KCP会不停的进行update更新最新情况,数据的实际发送和接收数据都是在update时进行的。

Python KCP使用实例

  • 想用Python调用KCP协议,发现并没有现成的实现,在github上找到一个用Cython包装的pyhton-kcp,却不支持Python3,于是做了简单的修改,使其支持Python3,地址如下:https://github.com/HatBoy/Python-KCP
  • 下层使用UDP协议,KCP的实例代码如下:
  • Server端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    #coding=UTF-8
    import socket
    import time
    from lkcp import KcpObj
    # send回调函数,真正发送数据是是用该函数发送
    def kcp_callback(uid, data):
    s.sendto(data, uid_addr[uid])
    # 用该函数接收UDP数据
    def recv_udp(sock):
    try:
    data, udp_addr = sock.recvfrom(65535)
    uid_addr[1] = udp_addr
    return data
    except Exception as e:
    pass
    return None
    # socket连接
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # 很重要一定要设置为非阻塞,不然程序会卡死在接受数据的地方
    s.setblocking(0)
    addr = ("127.0.0.1", 9999)
    s.bind(addr)
    # 存放连接客户端字典
    uid_addr = {}
    kcp = KcpObj(123, 1, kcp_callback)
    # 设置工作模式和窗口大小
    kcp.nodelay(1, 10, 2, 1)
    kcp.wndsize(128, 128)
    kcp.setmtu(1000)
    # 循环接收并发送数据
    while True:
    kcp.update(time.time())
    # 接收数据并吐给input函数
    while True:
    data = recv_udp(s)
    if data is None:
    break
    kcp.input(data)
    # 接收数据将数据send给客户端
    kcp.update(time.time())
    while True:
    lens, data = kcp.recv()
    if lens < 0:
    break
    print("Recv: ", data)
    kcp.send(data)
    time.sleep(0.05)
  • client端:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    #coding=UTF-8
    import time
    import socket
    import random
    from lkcp import KcpObj
    start_time = time.time()
    def getms():
    return int((time.time()-start_time)*1000)
    def kcp_callback(uid, data):
    s.sendto(data, addr)
    def recv_udp(sock):
    try:
    data, udp_addr = sock.recvfrom(65535)
    return data
    except Exception as e:
    pass
    return None
    # socket连接
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.setblocking(0)
    addr = ("127.0.0.1", 9999)
    kcp = KcpObj(123, 1, kcp_callback)
    kcp.nodelay(1, 10, 2, 1)
    kcp.wndsize(128, 128)
    kcp.setmtu(1000)
    start_ts = getms() # 获取代码运行一直到此时的时间差
    slap = start_ts
    while True:
    current = getms()
    kcp.update(time.time())
    while current >= slap: # 主动发送sleep()/100
    data = str(random.randint(0, 10000))
    print("Send: ", data)
    kcp.send(data)
    slap += 20
    kcp.update(time.time())
    while True:
    data = recv_udp(s)
    if data is None:
    break
    kcp.input(data)
    kcp.update(time.time())
    while True:
    lens, data = kcp.recv()
    if lens < 0:
    break
    print("Recv: ", data)
    kcp.send(data)
    time.sleep(0.05)
  • 运行Server端和Client端即可看到效果。

参考资料

文章目录
  1. 1. KCP协议介绍
  2. 2. KCP协议使用流程
  3. 3. Python KCP使用实例
    1. 3.1. 参考资料
|