我正在尝试概念性地通过我在c#(客户端和服务器端)编写的客户端 - 服务器套接字应用程序的模型。我的服务器需要同时处理许多客户端,最好是同时处理来自客户端的多个请求。我已经为我的通信设计了一个容器,我将在每条消息的开头发送一个固定长度的标题,其中包含(除其他外)消息的长度。我对c#中的套接字编程有一些经验,所以我很习惯使用异步套接字。
从概念上讲,我遇到的主要问题是我需要客户端和服务器随时都能接收消息。客户端将建立连接,并保持“登录”(如IM客户端),并且它将需要在任意时间接收数据并在任意时间发出请求。作为我的协议的一部分,我还希望收到对每个请求的响应(无论是从服务器到客户端,还是从客户端到服务器)。
如果可能,我希望能够使用单个插槽。我相信我可以使用两个套接字来完成这项工作,一个用于发出服务器 - >客户端请求,一个用于客户端 - >服务器请求,但我不希望处理两个端口/连接的额外复杂性。但是,使用单个套接字时,我不确定如何管理发送请求并在交错时获取响应。
我在搜索中找不到类似服务器或客户端的任何示例。感谢任何提供任何想法的人。
答案 0 :(得分:3)
套接字是双向的。您只需要一个套接字即可在两个端点之间来回发送数据。但是在客户端上,您可能需要让一个线程不断地从套接字读取并通知某个组件或对它接收的数据进行排队,以便在其他地方处理,而另一个线程通过同一个套接字发送数据。这样就可以进行异步通信。
但是在服务器端,您需要打开一个ServerSocket,它将绑定到TCP端口并接受该端口上的连接;您接受的每个连接都是一个新的Socket。所以每个客户端都有一个套接字,每个客户端可能需要一个线程。
您需要为将要通过套接字的消息建立协议。你可以自己制作协议,也可以查看一下,具体取决于你想做什么。通常,您将首先发送两个或四个字节,其长度为剩余的消息,因此另一方知道从流中读取多个字节;这是一条消息。消息的开头通常是消息类型;在一个非常简单的场景中,你可以说“1”是客户端请求,“2”是服务器响应,“3”是客户端回应,“4”是服务器回应响应,“5”是服务器echo,“6”是客户端回应响应等。这样,您收到一条消息,解析它,然后您知道它是来自客户端的请求或对服务器发送的消息的响应,例如。
答案 1 :(得分:2)
如果你正在使用TCP,那么你必须在服务器端为每个客户端提供一个套接字,以及监听连接的套接字。对于UDP,您可以使用一个套接字完成所有操作。无论如何,对于您的一个UDP套接字或每个TCP客户端套接字执行以下操作:
当您发生必要的事件时(或者用户按下按钮或者您需要执行某些操作的消息),BeginReceive
始终进行BeginWrite
。
编辑:在回复您的评论时,我处理它的方式是在标题中包含请求ID。每当发送请求(来自任一端)时,都包含该消息的唯一值。使用Dictionary<RequestId, RequestState>
(对类型进行适当替换)并查看传入消息是否与预先存在的请求相关。此外,您可以指定所有具有高位集的ID是源自客户端的请求,而高位清除的ID来自服务器以避免冲突:
Server Client
Request 0x00 -------------> Starts processing
Starts processing <------- (unrelated) Request 0x80
Request 0x00 complete <---- Sends response to 0x00
Sends response to 0x80 ---> Request 0x80 complete
这是AOL的OSCAR协议用于(可能很慢)有状态请求的系统。
EDIT2:嗯,好吧......你真的想阻止吗?您可以做的事情是有一个单独的线程处理Send
/ Receive
调用。该线程将通过线程安全的“发送消息”队列和类似的“消息接收”伪队列与主线程通信。我将后者称为伪队列的原因是您希望能够从队列中不按顺序接收消息。主线程会在发送队列上放置一条消息,然后阻塞接收队列。每次套接字线程更新接收队列时,主线程都会唤醒并检查它所需的消息是否存在。如果是,它将采取它(乱序)并完成所需的操作。如果消息还没有,它只会再次阻塞队列。当操作最终完成时,它只是恢复正常操作并从接收队列中按顺序获取消息并处理它们或执行其他任何操作。
这有什么意义吗?如果它有点令人困惑,我很抱歉...