重用TCP客户端和TCP服务器之间的一个打开的TCP连接

时间:2016-12-16 20:56:33

标签: javascript node.js sockets tcp

有第三方服务公开我的节点服务器(TCP客户端)应使用tls节点模块建立TCP连接的TCP服务器。 作为TCP客户端,节点服务器同时也是HTTP服务器,它应该像来自Web浏览器和第三方TCP服务器的客户之间的代理一样。因此,常见的用例是浏览器将HTTP请求发送到节点服务器,节点服务器将通过TCP套接字与TCP服务器进行通信,以收集/构建响应并将其发送回浏览器。

我当前的解决方案是,来自网络浏览器的每个客户/每个HTTP请求 将与TCP服务器建立新的分离的TCP连接。事实证明这个解决方案很糟糕,每次浪费时间进行SSL握手,TCP服务器不允许来自单个客户端的50多个并发连接。因此,使用此解决方案,不可能有超过50个客户同时与节点服务器通信。

在节点中使用tls模块执行此操作的标准方法是什么?

我要做的就是拥有一个单独的TCP连接,这个连接将始终处于活动状态,并且将在节点应用程序启动时建立连接,并且最重要的是此连接应该重新用于来自Web浏览器的许多 HTTP请求。

enter image description here

我首先关注的是如何根据来自TCP服务器的数据通过TCP原始套接字构建不同的HTTP响应。好的是,在描述应该在TCP服务器端采取哪些操作时,我可以通过标头向unique token发送一些内容。

socket.write(JSON.stringify({
  header: {uniqueToken: 032424242, type: 'create something bla bla'},
  data: {...}
}))

具有唯一令牌TCP服务器端保证,当来自通过TCP套接字和解析的不同块组合时,JSON将具有此uniqueToken,这意味着我能够将此JSON映射到HTTP请求和返回HTTP响应。

我的问题通常是TCP协议保证在这种情况下,不同的连续块将属于在组合和解析这些块时需要创建的相同响应(当'\n\n'发生时) 换句话说,有任何保证不会混合块。 (我知道包含'\n\n'的块可能属于两个不同的响应,但我可以处理它)

如果这不可能,我没有看到第一种解决方案(需要创建一个响应的一个连接)可以改进的方式。唯一的方法是引入一些连接池概念,据我所知tls模块没有提供任何开箱即用的方式。

编辑基于以下评论,简短版本的问题: 可以说TCP服务器在收到命令create something bla bla时需要2秒钟才能发送所有块 如果TCP客户端发送命令create something bla bla并且在1毫秒之后立即发送第二个create something bla bla,那么在写入所有之前,TCP服务器是否有可能写入与第二个命令相关的块strong>与第一个命令相关的块。

1 个答案:

答案 0 :(得分:1)

  

...在写入与第一个命令相关的所有块之前,TCP服务器是否有可能发生与第二个命令相关的块写入。

如果我正确理解了您的问题,您会询问服务器端的同一套接字上的write("AB")后跟write("CD")是否会导致客户端从服务器读取ACDB

如果两次写入都成功并且实际上已将所有数据写入底层套接字缓冲区,则情况并非如此。但是,由于TCP是没有隐式消息边界的流协议,因此客户端的读取可能类似于ABCDAB,后跟CDA,后跟{ {1}}后跟BC等。因此,要区分来自服务器的消息,您必须添加一些应用程序级别的消息检测,例如消息结束标记,大小前缀或类似内容。

另外,我将之前的语句限制为两次写入都成功并且实际上已将所有数据写入底层套接字缓冲区。情况不一定如此。例如,你可能会执行一个缓冲写操作的函数,如(在C中)fwrite而不是write。在这种情况下,您通常无法控制在哪个时间写入缓冲区的哪些部分,因此可能D会导致" A"写入插座时," B"保持在缓冲区。如果你有另一个缓冲的编写器使用相同的底层文件描述符(即套接字),但不是相同的缓冲区,那么你实际上最终会得到像fwrite("AB")这样的东西发送到底层套接字,从而发送给客户端。

如果未缓冲的写入没有完全成功,即如果ACDB只写了" A"并通过返回值发出信号" B"需要稍后写。如果你有一个线程之间同步不足的多线程应用程序,你可能会得到第一个线程发送的情况" A"在不完整的尝试中写入" AB",然后是另一个线程发送" CD"成功,然后第一个线程再次通过写" B"完成发送。在这种情况下,您最终也会得到" ACDB"在插座上。

总结:TCP层保证发送顺序与收到的订单相同,但用户空间(即应用程序)需要确保它确实以正确的顺序将数据发送到套接字。此外,TCP没有消息边界,因此需要在应用程序内部通过使用消息边界,长度前缀,固定消息大小等来实现TCP流内消息的区别。