如何创建持久 TCPSocket?
我有一个服务器连续向客户端发送两条消息:
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 中有一种方法可以做到这一点;我就是不知道怎么办。
所以,我的问题列表是:
- 如何创建持久服务器<->客户端连接,如上所述?
- 有没有更好的方法在不使用循环的情况下保持服务器打开?
- 为什么服务器中没有
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:
- How do I create a persistant Server <-> Client connection, as described above?
- Is there a better way of keeping a server open without using
loop
? - Why does it stay suspended like that without
client.close
in the server? Does the server buffer the messages before it sends them?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您有多种选择:您可以编写一个线程服务器,其中一个主要共享内存的“进程”执行多个执行线程,并且每个线程都有一些线程本地存储,例如
客户端
。或者,您可以编写一个多进程服务器,其中每个客户端连接通过 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 来编写您的服务器。 (无论如何,这可能会或可能不会更容易维护。)
无限循环很好:)但有时提供一种重新启动的方法是有意义的服务器或通过
signal(7)
或专用命令接口终止它们。啊,这里是您真正需要的问题 - TCP 将尝试缓冲来自服务器的多个数据包的结果,并且由于可能需要分段太大当数据包在路由中途时,作为客户端,您无法保证保留任何类型的数据报边界。
您的服务器的 TCP/IP 堆栈很可能在发送第一个数据包之前等待了一小会儿,以查看是否即将有更多数据到来。 (经常如此。)它可能将对
client.print()
的两次调用合并到一个 TCP 数据包中。您的客户端调用client.read()
两次,但可能只够一次读取数据。如果您希望 TCP 流立即发送第一个数据包,您可以考虑设置TCP_NODELAY
套接字选项来禁用这个小延迟。 (但它本身可能并没有什么帮助;您可能需要将它与 TCP_CORK 结合使用来尝试“限制”所有写入,直到您显式刷新它们。这可能是一个糟糕的主意.)更好的选择是重写您的客户端以了解服务器和客户端之间的协议;它需要将输入读入缓冲区,解析缓冲区以查找“完整消息”,使用这些消息,然后返回到其主事件循环。
也许您的协议是读/写以
"\n"
字符结尾的 ASCII 或 UTF-8;在这种情况下,您可以将print
更改为puts
或在正确的位置添加\n
,然后更改read()< /code> 到
readline()
。 (这将使标准 IO 库为您处理您的协议。)另一种方法是发送带有长度+数据对的数据,并等待正确的数据长度。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 viafork(2)
(which pretty much means it runs on Unix-only, as Windows attempts to implementfork(2)
never work well). Or, you could write an event-loop based state machine, and useselect(2)
orpoll(2)
orepoll(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.)
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.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 callsclient.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 theTCP_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 withTCP_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 changeprint
toputs
or add the\n
in the right places, and changeread()
toreadline()
. (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.使用看跌期权代替。如果你想确定它已发送,请使用socket.flush。否则它会缓冲数据。
Use puts instead. And use socket.flush if you want to male sure it's sent. It buffers the data otherwise.