2

我试图在概念上通过我用 c#(客户端和服务器)编写的客户端-服务器套接字应用程序的模型来工作。我的服务器需要同时处理多个客户端,最好是同时处理来自客户端的多个请求。我已经为我的通信制定了一个容器,我将在每条消息的开头发送一个固定长度的标头,其中包含(除其他外)消息的长度。我有一些使用 c# 进行套接字编程的经验,所以我很喜欢使用异步套接字。

从概念上讲,我遇到的主要问题是我需要客户端和服务器都能够随时接收消息。客户端将建立连接,并保持“登录”状态(就像 IM 客户端一样),它需要在任意时间接收数据并在任意时间发出请求。作为协议的一部分,我还希望接收对每个请求的响应(无论是从服务器到客户端,还是从客户端到服务器)。

如果可能的话,我希望能够使用单个套接字。我相信我可以使用两个套接字来完成这项工作,一个用于发出服务器->客户端请求,一个用于客户端->服务器请求,但我不希望处理两个端口/连接的额外复杂性。但是,使用单个套接字,我不确定如何管理发送请求并在可以交错时获取响应。

我在搜索中找不到任何类似服务器或客户端的示例。感谢任何提供任何想法的人。

4

2 回答 2

3

套接字是双向的。您只需要一个套接字即可在两个端点之间来回发送数据。但是在客户端上,您可能需要一个线程不断地从套接字读取并通知某些组件或将它接收到的数据排队,以便在其他地方处理,而另一个线程通过同一个套接字发送数据。这样你就有了异步通信。

但是在服务器端,您需要打开一个 ServerSocket,它将绑定到 TCP 端口并接受该端口上的连接;您接受的每个连接都是一个新的 Socket。因此,每个客户端都有一个套接字,并且每个客户端可能需要一个线程。

您需要为将通过套接字传递的消息建立协议。您可以自己制作协议或查找协议,具体取决于您想要做什么。通常你会先发送两个或四个字节以及消息其余部分的长度,这样对方就知道从流中读取那么多字节;那是一条信息。消息的开头通常是消息类型;在一个非常简单的场景中,您可以说“1”是客户端请求,“2”是服务器响应,“3”是客户端回显,“4”是服务器回显响应,“5”是服务器echo,“6”是客户端回显响应等。例如,您接收消息,解析它然后您知道它是来自客户端的请求或对从服务器发送的消息的响应。

于 2009-05-19T22:50:16.920 回答
2

如果您使用 TCP,那么服务器端的每个客户端都必须有一个套接字,以及用于侦听连接的套接字。对于 UDP,您可以使用一个套接字来完成这一切。无论如何,为您的一个 UDP 套接字或每个 TCP 客户端套接字执行以下操作:

BeginReceive随时进行,当您BeginWrite发生必要的事件时(即用户按下按钮或收到您需要做某事的消息)。

编辑:为了回应您的评论,我处理它的方式是在您的标头中包含一个请求 ID。每当发送请求(从任一端)时,都包含该消息的唯一值。使用 a 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来电。该线程将通过线程安全的“发送消息”队列和类似的“收到消息”伪队列与主线程通信。我将后者称为伪队列的原因是您希望能够从队列中获取乱序消息。主线程将消息放入发送队列,然后阻塞接收队列。每次套接字线程更新接收队列时,主线程都会唤醒并检查它想要的消息是否在那里。如果是,它将接受它(无序)并完成所需的操作。如果消息还没有,它只会再次阻塞在队列中。当操作最终完成时,它将恢复正常操作并按顺序从接收队列中获取消息并处理它们或执行其他任何操作。

这有任何意义吗?很抱歉,如果它有点令人困惑......

于 2009-05-19T21:41:56.933 回答