测速逻辑

测速原理

往一个测速服务器post发送chunked数据,固定发到多少为止,可以采用多线程进行发送,统计发送的时间,这个时间是客户端的,仅仅作为参考,实际最后的测速结果是由服务端给出的。
要发送分块编码(chunked)格式的数据,可以使用以下步骤:

  1. 设置HTTP请求头中的Transfer-Encoding字段为chunked。
  2. 将数据分成多个块(chunk),每个块都包含一个长度和数据本身。
  3. 将每个块发送到服务器,以回车换行符(\r\n)作为分隔符。
  4. 在所有块发送完成后,发送一个长度为0的块作为结束符,以回车换行符(\r\n)结尾。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 发送请求头
    s.sendall(b'POST /api HTTP/1.1\r\n')
    s.sendall(b'Host: example.com\r\n')
    s.sendall(b'Content-Type: text/plain\r\n')
    s.sendall(b'Transfer-Encoding: chunked\r\n')
    s.sendall(b'\r\n')

    # 发送第一个块
    chunk1 = bytes('{:X}\r\n'.format(len(data1)), 'utf-8') + data1
    s.sendall(chunk1)

    # 发送第二个块
    chunk2 = bytes('{:X}\r\n'.format(len(data2)), 'utf-8') + data2
    s.sendall(chunk2)

    # 发送结束块
    s.sendall(b'0\r\n\r\n')

但测速可以不完全按照这个格式,只需要固定Transfer-Encoding: chunked就行,因为客户端是一直发数据,知道发到指定大小才停止发送,测速服务器在间隔1s没有接收到数据后,会告诉客户端的测速结果,等于是结束条件由服务端来决定了。

实现

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
if (remote[i].events & EPOLLOUT)
{
while (sendSize < totalSize)
{
result = send(sock, postData, min(postDataBufSize, totalSize - sendSize), 0);
if (-1 == result)
{
if (errno != EAGAIN && errno != EINTR)
{
goto end;
}
else if (!iter->second.eagain)
{
iter->second.eagain = true;
}
break;
}
if (iter->second.eagain)
{
sendSize += result;
}
iter->second.sendSize += result;
}
if (sendSize >= totalSize)
{
struct epoll_event event = {0};
event.data.fd = sock;
event.events = EPOLLIN | EPOLLERR | EPOLLET;
epoll_ctl(epollFd, EPOLL_CTL_MOD, sock, &event);
}
}
if (remote[i].events & EPOLLIN)
{
iter = connMap.find(sock);
assert(iter != connMap.end());
result = recv(sock, recvBuf, sizeof(recvBuf), 0);
if (0 == result || (-1 == result && errno != EAGAIN && errno != EINTR))
{
printf("socket(%d) error = %s\n", sock, strerror(errno));
}
printf("socket(%d) recv %s\n", sock, recvBuf);
memset(recvBuf, 0, sizeof(recvBuf));
goto end;
}
if (remote[i].events & EPOLLERR)
{
printf("socket(%d) error = %s\n", sock, strerror(errno));
iter = connMap.find(sock);
goto end;
}
continue;
end:
epoll_ctl(epollFd, EPOLL_CTL_DEL, sock, NULL);
close(sock);
connMap.erase(iter);
nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!