任意操纵 TCP 连接流会导致问题吗?
Does arbitrary manipulation of TCP connection flow cause problems?
当我的 HTTP 代理是一个简单的 multi-threaded 代理时,GET 和 CONNECT 请求运行良好:现在我想在客户端和服务器之间的 TCP 对话中间放置一个 queue ing 机制,其中来自客户端和服务器的字节按优先级 queues 推送,只有具有最高优先级的数据才会从 queues 中弹出并发送到目的地。
我从客户端和服务器 recv()
ing 获得的有效数据与一些元数据一起存储在 struct infoPkt
中:
typedef struct infoPacket {
int clientFd;
int serverFd;
int request;
int direction;
int priority;
char payload[MAX_PAYLOAD_SIZE];
int payloadSize;
in_addr_t serveraddr;
char host[256];
std::chrono::time_point<std::chrono::system_clock> start;
bool first;
bool last;
} infoPkt;
我不能post这里所有的代码,太多了。我将尝试在我的程序中解释数据包 infoPkt
的流程:
我有以下 classes:
ClientManager
:使用 accept()
循环,从客户端获取 HTTP 请求,在其中创建此连接的第一个 infoPkt
请求并将其推送prio queues DequeManager::getInstance().insert(newElem);
,再次开始 accept()
ing;
HTTPManager
:这个class,里面的方法都是static
,处理客户端和服务端实际的数据交换; HTTPManager::dealPkt(infoPkt p)
从 DequeManager
调用,将其关联到 queueingThread
线程(请参阅下面的 DequeManager
),当连接的第一个数据包被删除时;将 200 OK
发送到客户端后,我循环使用 select()
客户端和服务器 fds 来检查传入数据;我recv()
,我在prio queues中插入DequeManager
;如果我 recv()
来自客户端或服务器的 0 个字节,我会制作一个 infoPkt
没有有效负载但 infoPkt.last
设置为 true
和 break
select()
循环;
DequeManager
:这里有两个线程在工作,一个正在循环检查饥饿(如果一个数据包在queue中花费了太多时间,它将被移入a queue 具有更高的优先级)而另一个总是首先从具有更高优先级的 queue 中删除数据包,然后当较高的优先级为空时从具有较低优先级的 queue 中删除数据包;当线程删除一个 infoPkt
时,我们有两种情况:
serverFd
in infoPkt
为-1:表示这是HTTP请求包,还没有建立连接,因为我没有关联服务器文件描述符;删除线程对远程服务器执行 socket()
、bind()
和 connect()
,将新服务器 fd 设置为 infoPkt.serverFd
,调用 std::thread queueingThread(HTTPManager::dealPkt, packet); queuingThread.detach();
并继续删除;
serverFd
in infoPkt
is > 0: 这意味着这是一个 infoPkt
属于已经存在的连接,被推入 queue客户端或服务器;首先我检查 infoPkt.last
标志,如果它是 true
这是连接的最后一个数据包,来自客户端或服务器的 recv()
ing 返回 0 所以我制作了这个 infoPkt
没有payload
但将 infoPkt.last
设置为 true
,所以我执行 close(infoPkt.clientFd);
和 close(infoPkt.serverFd);
;否则,如果 infoPkt.serverFd
> 0 并且 infoPkt.last
不是 true
,则此数据包将被发送到目的地检查 infoPkt.direction
.
我在标题中写的"flow manipulation"指的是select()
中的数据交换,在CONNECT HTTP连接中:我从客户端获取的数据不是立即发送到服务器,而是推送在 prio queues 和稍后发送时,仅当删除线程将删除该数据时,因为根据优先级机制,满足条件告诉它轮到它了。虽然我以前的代理在接收到数据时立即将数据转发到目的地,但一切顺利:我试图在两者之间设置优先级机制,延迟数据交换,但程序没有按我预期的那样工作;连接在发送一两个数据包后自行关闭,我不明白为什么。
这是我尝试连接到 Netflix 主页时程序的输出。
Created ClientManager
Start PROXY, thread id 67426496
Number of interfaces (or their IP addr) has changed, mmap() again
Found new interfaces: wlan0[192.168.1.88]
priority 1 to hostname www.netflix.com
priority 1 to hostname nflxvideo.net
priority 2 to hostname www.youtube.com
priority 2 to hostname googlevideo.com
priority 3 to hostname www.facebook.com
remover thread with method DequeManager::removeAll() detached
controller thread with method DequeManager::aging() detached
67426496 THREAD MAIN
Proxy listening to port 8000, here we go!
67426496 ClientManager::getRequestFromClient received from client 215 byte
67426496 ClientManager::manageClient() inserted C 4, S -1, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 1, last 0 --->
CONNECT www.netflix.com:443 HTTP/1.1
Host: www.netflix.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
67426496 ClientManager::getRequestFromClient received from client 215 byte
67426496 ClientManager::manageClient() inserted C 5, S -1, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 1, last 0 --->
CONNECT www.netflix.com:443 HTTP/1.1
Host: www.netflix.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
111642368 DequeManager::removeAndDealPkt created pair (4 7)
137008896 HTTPManager::dealPkt() thread 137008896 started
137008896 HTTPManager::dealPkt() dealing with socket fd pair (4 7)
137008896 HTTPManager::dealPkt [CONNECT] sent 200 OK to client
137008896 HTTPManager::dealPkt [CONNECT] max fd+1 between client 4 and server 7: 8
137008896 HTTPManager::dealPkt [CONNECT] queued up pkt from client, 209 bytes
111642368 DequeManager::removeAndDealPkt created pair (5 8)
111642368 DequeManager::removeAndDealPkt send() C 4, S 7, CONNECT, UPLOAD, prio 1, size 209, host www.netflix.com, first 0, last 0
145401600 HTTPManager::dealPkt() thread 145401600 started
145401600 HTTPManager::dealPkt() dealing with socket fd pair (5 8)
145401600 HTTPManager::dealPkt [CONNECT] sent 200 OK to client
145401600 HTTPManager::dealPkt [CONNECT] max fd+1 between client 5 and server 8: 9
111642368 DequeManager::removeAndDealPkt sent_bytes 209
145401600 HTTPManager::dealPkt [CONNECT] queued up pkt from client, 209 bytes
111642368 DequeManager::removeAndDealPkt send() C 5, S 8, CONNECT, UPLOAD, prio 1, size 209, host www.netflix.com, first 0, last 0
111642368 DequeManager::removeAndDealPkt sent_bytes 209
137008896 HTTPManager::dealPkt [CONNECT] recv() 0 from client, crafting last packet 4 7
137008896 HTTPManager::dealPkt dealPkt() thread terminated
145401600 HTTPManager::dealPkt [CONNECT] recv() 0 from client, crafting last packet 5 8
145401600 HTTPManager::dealPkt dealPkt() thread terminated
111642368 DequeManager::removeAndDealPkt get LAST PACKET, closing fds 4 and 7
111642368 DequeManager::removeAndDealPkt get LAST PACKET, closing fds 5 and 8
67426496 ClientManager::getRequestFromClient received from client 215 byte
67426496 ClientManager::manageClient() inserted C 6, S -1, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 1, last 0 --->
CONNECT www.netflix.com:443 HTTP/1.1
Host: www.netflix.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
111642368 DequeManager::removeAndDealPkt created pair (6 5)
145401600 HTTPManager::dealPkt() thread 145401600 started
145401600 HTTPManager::dealPkt() dealing with socket fd pair (6 5)
145401600 HTTPManager::dealPkt [CONNECT] sent 200 OK to client
145401600 HTTPManager::dealPkt [CONNECT] max fd+1 between client 6 and server 5: 7
145401600 HTTPManager::dealPkt [CONNECT] queued up pkt from client, 215 bytes
111642368 DequeManager::removeAndDealPkt send() C 6, S 5, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 0, last 0
111642368 DequeManager::removeAndDealPkt sent_bytes 215
145401600 HTTPManager::dealPkt [CONNECT] recv() 0 from client, crafting last packet 6 5
145401600 HTTPManager::dealPkt dealPkt() thread terminated
111642368 DequeManager::removeAndDealPkt get LAST PACKET, closing fds 6 and 5
^CCaught signal 2
如果有人说"this is wrong because data exchange in TCP connection is not expected to be manually delayed from within with a priority mechanism of yours",我就扔掉我的代码,放下心来,另辟蹊径实现TCP连接中的流量控制,一个包一个包;真诚地,我更希望有人对我说 "you moron, just check this and everything will work",所以我知道我不会在我的这个业余项目上浪费几个月 :D
编辑:我只提供了我项目中的重要代码,如果您有任何问题,请随时提出。 Here's my Gist
问题是 TCP 有一个明确的机制来修复 重新排序问题。这可能涉及强制发送方重新发送数据包的 NACK。
您 "priority" 对 TCP 流中字节的重新排序最多 会创建大量重新发送数据,因此您的 "priority" 会降低一切。但是很有可能当 TCP 流被破坏得太厉害时,任何一方都放弃了。
当我的 HTTP 代理是一个简单的 multi-threaded 代理时,GET 和 CONNECT 请求运行良好:现在我想在客户端和服务器之间的 TCP 对话中间放置一个 queue ing 机制,其中来自客户端和服务器的字节按优先级 queues 推送,只有具有最高优先级的数据才会从 queues 中弹出并发送到目的地。
我从客户端和服务器 recv()
ing 获得的有效数据与一些元数据一起存储在 struct infoPkt
中:
typedef struct infoPacket {
int clientFd;
int serverFd;
int request;
int direction;
int priority;
char payload[MAX_PAYLOAD_SIZE];
int payloadSize;
in_addr_t serveraddr;
char host[256];
std::chrono::time_point<std::chrono::system_clock> start;
bool first;
bool last;
} infoPkt;
我不能post这里所有的代码,太多了。我将尝试在我的程序中解释数据包 infoPkt
的流程:
我有以下 classes:
ClientManager
:使用accept()
循环,从客户端获取 HTTP 请求,在其中创建此连接的第一个infoPkt
请求并将其推送prio queuesDequeManager::getInstance().insert(newElem);
,再次开始accept()
ing;HTTPManager
:这个class,里面的方法都是static
,处理客户端和服务端实际的数据交换;HTTPManager::dealPkt(infoPkt p)
从DequeManager
调用,将其关联到queueingThread
线程(请参阅下面的DequeManager
),当连接的第一个数据包被删除时;将200 OK
发送到客户端后,我循环使用select()
客户端和服务器 fds 来检查传入数据;我recv()
,我在prio queues中插入DequeManager
;如果我recv()
来自客户端或服务器的 0 个字节,我会制作一个infoPkt
没有有效负载但infoPkt.last
设置为true
和break
select()
循环;DequeManager
:这里有两个线程在工作,一个正在循环检查饥饿(如果一个数据包在queue中花费了太多时间,它将被移入a queue 具有更高的优先级)而另一个总是首先从具有更高优先级的 queue 中删除数据包,然后当较高的优先级为空时从具有较低优先级的 queue 中删除数据包;当线程删除一个infoPkt
时,我们有两种情况:serverFd
ininfoPkt
为-1:表示这是HTTP请求包,还没有建立连接,因为我没有关联服务器文件描述符;删除线程对远程服务器执行socket()
、bind()
和connect()
,将新服务器 fd 设置为infoPkt.serverFd
,调用std::thread queueingThread(HTTPManager::dealPkt, packet); queuingThread.detach();
并继续删除;serverFd
ininfoPkt
is > 0: 这意味着这是一个infoPkt
属于已经存在的连接,被推入 queue客户端或服务器;首先我检查infoPkt.last
标志,如果它是true
这是连接的最后一个数据包,来自客户端或服务器的recv()
ing 返回 0 所以我制作了这个infoPkt
没有payload
但将infoPkt.last
设置为true
,所以我执行close(infoPkt.clientFd);
和close(infoPkt.serverFd);
;否则,如果infoPkt.serverFd
> 0 并且infoPkt.last
不是true
,则此数据包将被发送到目的地检查infoPkt.direction
.
我在标题中写的"flow manipulation"指的是select()
中的数据交换,在CONNECT HTTP连接中:我从客户端获取的数据不是立即发送到服务器,而是推送在 prio queues 和稍后发送时,仅当删除线程将删除该数据时,因为根据优先级机制,满足条件告诉它轮到它了。虽然我以前的代理在接收到数据时立即将数据转发到目的地,但一切顺利:我试图在两者之间设置优先级机制,延迟数据交换,但程序没有按我预期的那样工作;连接在发送一两个数据包后自行关闭,我不明白为什么。
这是我尝试连接到 Netflix 主页时程序的输出。
Created ClientManager
Start PROXY, thread id 67426496
Number of interfaces (or their IP addr) has changed, mmap() again
Found new interfaces: wlan0[192.168.1.88]
priority 1 to hostname www.netflix.com
priority 1 to hostname nflxvideo.net
priority 2 to hostname www.youtube.com
priority 2 to hostname googlevideo.com
priority 3 to hostname www.facebook.com
remover thread with method DequeManager::removeAll() detached
controller thread with method DequeManager::aging() detached
67426496 THREAD MAIN
Proxy listening to port 8000, here we go!
67426496 ClientManager::getRequestFromClient received from client 215 byte
67426496 ClientManager::manageClient() inserted C 4, S -1, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 1, last 0 --->
CONNECT www.netflix.com:443 HTTP/1.1
Host: www.netflix.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
67426496 ClientManager::getRequestFromClient received from client 215 byte
67426496 ClientManager::manageClient() inserted C 5, S -1, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 1, last 0 --->
CONNECT www.netflix.com:443 HTTP/1.1
Host: www.netflix.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
111642368 DequeManager::removeAndDealPkt created pair (4 7)
137008896 HTTPManager::dealPkt() thread 137008896 started
137008896 HTTPManager::dealPkt() dealing with socket fd pair (4 7)
137008896 HTTPManager::dealPkt [CONNECT] sent 200 OK to client
137008896 HTTPManager::dealPkt [CONNECT] max fd+1 between client 4 and server 7: 8
137008896 HTTPManager::dealPkt [CONNECT] queued up pkt from client, 209 bytes
111642368 DequeManager::removeAndDealPkt created pair (5 8)
111642368 DequeManager::removeAndDealPkt send() C 4, S 7, CONNECT, UPLOAD, prio 1, size 209, host www.netflix.com, first 0, last 0
145401600 HTTPManager::dealPkt() thread 145401600 started
145401600 HTTPManager::dealPkt() dealing with socket fd pair (5 8)
145401600 HTTPManager::dealPkt [CONNECT] sent 200 OK to client
145401600 HTTPManager::dealPkt [CONNECT] max fd+1 between client 5 and server 8: 9
111642368 DequeManager::removeAndDealPkt sent_bytes 209
145401600 HTTPManager::dealPkt [CONNECT] queued up pkt from client, 209 bytes
111642368 DequeManager::removeAndDealPkt send() C 5, S 8, CONNECT, UPLOAD, prio 1, size 209, host www.netflix.com, first 0, last 0
111642368 DequeManager::removeAndDealPkt sent_bytes 209
137008896 HTTPManager::dealPkt [CONNECT] recv() 0 from client, crafting last packet 4 7
137008896 HTTPManager::dealPkt dealPkt() thread terminated
145401600 HTTPManager::dealPkt [CONNECT] recv() 0 from client, crafting last packet 5 8
145401600 HTTPManager::dealPkt dealPkt() thread terminated
111642368 DequeManager::removeAndDealPkt get LAST PACKET, closing fds 4 and 7
111642368 DequeManager::removeAndDealPkt get LAST PACKET, closing fds 5 and 8
67426496 ClientManager::getRequestFromClient received from client 215 byte
67426496 ClientManager::manageClient() inserted C 6, S -1, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 1, last 0 --->
CONNECT www.netflix.com:443 HTTP/1.1
Host: www.netflix.com:443
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
111642368 DequeManager::removeAndDealPkt created pair (6 5)
145401600 HTTPManager::dealPkt() thread 145401600 started
145401600 HTTPManager::dealPkt() dealing with socket fd pair (6 5)
145401600 HTTPManager::dealPkt [CONNECT] sent 200 OK to client
145401600 HTTPManager::dealPkt [CONNECT] max fd+1 between client 6 and server 5: 7
145401600 HTTPManager::dealPkt [CONNECT] queued up pkt from client, 215 bytes
111642368 DequeManager::removeAndDealPkt send() C 6, S 5, CONNECT, UPLOAD, prio 1, size 215, host www.netflix.com, first 0, last 0
111642368 DequeManager::removeAndDealPkt sent_bytes 215
145401600 HTTPManager::dealPkt [CONNECT] recv() 0 from client, crafting last packet 6 5
145401600 HTTPManager::dealPkt dealPkt() thread terminated
111642368 DequeManager::removeAndDealPkt get LAST PACKET, closing fds 6 and 5
^CCaught signal 2
如果有人说"this is wrong because data exchange in TCP connection is not expected to be manually delayed from within with a priority mechanism of yours",我就扔掉我的代码,放下心来,另辟蹊径实现TCP连接中的流量控制,一个包一个包;真诚地,我更希望有人对我说 "you moron, just check this and everything will work",所以我知道我不会在我的这个业余项目上浪费几个月 :D
编辑:我只提供了我项目中的重要代码,如果您有任何问题,请随时提出。 Here's my Gist
问题是 TCP 有一个明确的机制来修复 重新排序问题。这可能涉及强制发送方重新发送数据包的 NACK。
您 "priority" 对 TCP 流中字节的重新排序最多 会创建大量重新发送数据,因此您的 "priority" 会降低一切。但是很有可能当 TCP 流被破坏得太厉害时,任何一方都放弃了。