如何创建持久 TCPSocket?

发布于 2024-11-09 21:26:24 字数 846 浏览 0 评论 0原文

我有一个服务器连续向客户端发送两条消息:

require 'socket'
require 'thread'
connections = []
server = TCPServer.new(9998)
loop do
  Thread.start(server.accept) do |client|
    client.print 'Once'
    client.print 'Upon a time.'
  end #eo Thread
end #eo infinte loop

客户端是:

require 'socket'
client = TCPSocket.new('localhost', 9998)
2.times { print client.read }
client.close

客户端“处于暂停状态”,直到我关闭服务器,然后才打印出消息。我知道向服务器添加 client.close 可以解决暂停问题,但我不想关闭套接字。

我知道有些应用程序重用 TCPSocket。所以,它可以是 Client >服务器服务器>客户端客户端>服务器x2,等等。我确信 Ruby 中有一种方法可以做到这一点;我就是不知道怎么办。

所以,我的问题列表是:

  1. 如何创建持久服务器<->客户端连接,如上所述?
  2. 有没有更好的方法在不使用循环的情况下保持服务器打开?
  3. 为什么服务器中没有 client.close 时它会保持暂停状态?服务器在发送消息之前是否缓冲消息?

I have a server which sends two messages to the client in a row:

require 'socket'
require 'thread'
connections = []
server = TCPServer.new(9998)
loop do
  Thread.start(server.accept) do |client|
    client.print 'Once'
    client.print 'Upon a time.'
  end #eo Thread
end #eo infinte loop

The client is:

require 'socket'
client = TCPSocket.new('localhost', 9998)
2.times { print client.read }
client.close

The client 'stands suspended' until I shut down the server, and only then prints out the messages. I know adding client.close to the server would fix the suspension, but I don't want to close the socket.

I know some applications reuse a TCPSocket. So, it can be Client > Server, Server > Client, Client > Server x2, and so on. I am sure there's a way to do that in Ruby; I just can't figure out how.

So, my list of questions is:

  1. How do I create a persistant Server <-> Client connection, as described above?
  2. Is there a better way of keeping a server open without using loop?
  3. Why does it stay suspended like that without client.close in the server? Does the server buffer the messages before it sends them?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

凉墨 2024-11-16 21:26:24
  1. 如何创建持久服务器<->客户端连接,如上所述。

您有多种选择:您可以编写一个线程服务器,其中一个主要共享内存的“进程”执行多个执行线程,并且每个线程都有一些线程本地存储,例如客户端。或者,您可以编写一个多进程服务器,其中每个客户端连接通过 fork(2) 获取自己的独立进程(这几乎意味着它仅在 Unix 上运行,因为 Windows 试图实现< code>fork(2) 永远无法正常工作)。或者,您可以编写基于事件循环的状态机,并使用 select(2)poll(2)epoll(4)管理哪些客户端可读、可写和有错误。

在它们之间进行选择可能看起来很困难,但是有可用的指南。我想建议,小型系统(预计只有 20-30 个并发客户端)可以通过线程或分叉服务器得到很好的处理。如果您希望超过同时客户端的数量,您应该使用 EventMachine 或 < a href="http://monkey.org/~provos/libevent/" rel="nofollow">libevent 来编写您的服务器。 (无论如何,这可能会或可能不会更容易维护。)

2)是否有更好的方法来保持服​​务器打开,而不使用循环[因为我觉得我通过创建无限循环犯了谋杀罪]

无限循环很好:)但有时提供一种重新启动的方法是有意义的服务器或通过 signal(7) 或专用命令接口终止它们。

3) 为什么服务器中没有 client.close 时它会保持挂起状态?服务器在发送消息之前是否缓冲消息?

啊,这里是您真正需要的问题 - TCP 将尝试缓冲来自服务器的多个数据包的结果,并且由于可能需要分段太大当数据包在路由中途时,作为客户端,您无法保证保留任何类型的数据报边界。

您的服务器的 TCP/IP 堆栈很可能在发送第一个数据包之前等待了一小会儿,以查看是否即将有更多数据到来。 (经常如此。)它可能将对 client.print()两次调用合并到一个 TCP 数据包中。您的客户端调用 client.read() 两次,但可能只够一次读取数据。如果您希望 TCP 流立即发送第一个数据包,您可以考虑设置 TCP_NODELAY 套接字选项来禁用这个小延迟。 (但它本身可能并没有什么帮助;您可能需要将它与 TCP_CORK 结合使用来尝试“限制”所有写入,直到您显式刷新它们。这可能是一个糟糕的主意.)

更好的选择是重写您的客户端以了解服务器和客户端之间的协议;它需要将输入读入缓冲区,解析缓冲区以查找“完整消息”,使用这些消息,然后返回到其主事件循环。

也许您的协议是读/写以 "\n" 字符结尾的 ASCII 或 UTF-8;在这种情况下,您可以将 print 更改为 puts 或在正确的位置添加 \n,然后更改 read()< /code> 到 readline()。 (这将使标准 IO 库为您处理您的协议。)另一种方法是发送带有长度+数据对的数据,并等待正确的数据长度。

  1. How does one create a persistant Server <-> Client connection,as described above.

You've got several choices: you can write a threaded server, where a single 'process' with mostly shared memory executes multiple threads of execution, and each thread has some thread-local storage for e.g. client. Or, you could write a multi-process server, where each client connection gets its own independent process via fork(2) (which pretty much means it runs on Unix-only, as Windows attempts to implement fork(2) never work well). Or, you could write an event-loop based state machine, and use select(2) or poll(2) or epoll(4) to manage which clients are readable, writable, and have errors.

Choosing between them might seem difficult, but there are guidelines available. I'd like to suggest that -small- systems, where you're only expected to have 20-30 simultaneous clients, can be very well handled via threaded or forked servers. If you wish to go beyond that number of simultaneous clients, you should use a tool like EventMachine or libevent to write your server. (Which may or may not be easier to maintain anyway.)

2) Is there a better way of keeping a server open, without using loop [because I feel like I've commited murder by creating an endless loop]

Endless loops are quite fine :) but sometimes it makes sense to offer a way to restart servers or terminate them via signal(7)s or specialized command interfaces.

3) Why does it stay suspended like that without client.close in the server? Does the server buffer the messages before it sends them?

Ah, here is the question you really needed -- TCP will try to buffer the results of multiple packets from the server, and due to the potential need to fragment too-large packets mid-route, you, as a client, have no guarantees that any kind of datagram boundaries are preserved.

Chances are very good that your server's TCP/IP stack waited a tiny bit before sending the first packet to see if more data is coming soon. (It often does.) It probably coalesced the two calls to client.print() into a single TCP packet. Your client calls client.read() twice, but there is probably only enough data for one read. If you wanted your TCP stream to send the first packet immediately, you could look into setting the TCP_NODELAY socket option to disable this small delay. (But it might not help all on its own; you might need to use it in conjunction with TCP_CORK to try to "bottle up" all the writes until you explicitly flush them. This is probably an awful idea.)

A better option would be to re-write your client to be aware of the protocol between your server and client; it needs to read input into a buffer, parse the buffer to find 'complete messages', consume those messages, and then return to its main event loop.

Maybe your protocol is to read / write ASCII or UTF-8 terminated with "\n" characters; in that case, you can change print to puts or add the \n in the right places, and change read() to readline(). (This would let the standard IO library handle your protocol for you.) Another approach would be to send data with length+data pairs, and await the correct length of data.

叹沉浮 2024-11-16 21:26:24

使用看跌期权代替。如果你想确定它已发送,请使用socket.flush。否则它会缓冲数据。

Use puts instead. And use socket.flush if you want to male sure it's sent. It buffers the data otherwise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文