异步socket

转载请原作者联系

1.   Overview of Socket in .Net

如果做大型的分布式应用,且要求有很高的实时性,通常我们会使用TCP/IP协议来让clientserver进行通信——传递命令和数据(比如XML Stream)。这个时候我们就需要使用异步socket了。.Net Framework提供了Socket类,此类对WinSock进行了比较好的包装,隐藏了很多细节,大大简化了我们需要做的工作;另外还提供了更高层次的抽象的类——TcpListener类和TcpClient类。而且,.Net Framework还提供了NetworkStream来简化读取和发送数据的工作。但是TcpListenerTcpClientNetworkStream都是使用blocking call的方式,而这并不是我们想要的——我们需要异步的方式——高实时性要求我们必须使用异步Socket。再者,我们还要注意到TCP协议的特点——它不会帮我们区分消息的边界——比如我们分别发送了10K20K字节的数据出去,到了接收端的缓冲区中,这两条消息将是30K字节的一大块数据——这就需要我们自己建立一个机制来区分不同的消息。相对的,UDP协议将会为我们做区分消息边界的工作,就省去了区分不同的消息的工作。所以我们在做使用TCP协议通信的程序的时候,我们通常需要先定一个自己的通信协议。另外,还有一些实现上的细节需要注意的:多线程、server如何支持连接无限数量(当然是有限资源范围内的无限)的client、如何确定client已经断开了socket连接、如何区别client socket连接和server如何给特定的client发送信息,是否需要Queue来暂存消息等等问题。

2.   Consider and Discuss

其实上面已经提到了一些需要考量的问题。我把我认为需要考虑的问题列举如下:

1、  需要适应的网络环境。比如局域网、广域网和InternetInternet环境中通常需要考虑穿透防护墙问题,秘密通信问题(加密),而局域网环境中通常不需要考虑防护墙问题。

2、  Server支持连接无限量的client,且能区分开每个client和给特定的client发送消息。

3、  缓存消息。比如使用消息队列来存放消息。如果客户端数量非常大,那使用队列(能把消息存放到磁盘中)来保存消息(这些消息是需要业务模块去处理的)就有必要了,这样即使down机了也能恢复回去。

4、  Serverclient的通信协议。这里的协议不是指TCP这样的协议,而是我们的应用中约定的协议,比如消息格式。

5、  如何使用线程,即如何管理线程来实现异步通信。

6、  如何确定client已经断开了socket连接。

3.   Implement Asynchronous Socket

就我自己的了解,实现异步Socket大致有四种方法:

1、  使用Socket类的异步方法。

2、  使用Socket类的同步方法,但自己创建Thread来实现异步通信。

3、  使用Socket类的同步方法,但使用.Net FrameworkThreadPool类来实现异步通信。

4、  使用Socket类的同步方法,但使用自己写的ThreadPool来实现异步通信。

下面就这几种方法分别说明之。

3.1. AsyncCallback Method

此种方法可能是大家用的比较多的一种方法,也是一种最简单的方法。MSDN中讲如何实现异步socket的时候就讲的是使用AsyncCallback Method——使用socket类提供的BeginXXXEndXXX这样的方法。这样方法实际上都是使用了线程池——.net framework中的ThreadPool类。此类在一个Process中只能有一个实例,任何BeginXXXEndXXX方法都使用了此类,默认最大线程数量是25个,当然也可以修改此限制,具体做法为请看这里

下面是一些AsyncCallback Method的例子:

MSDN上的例子

CodeGuru上的例子

CodeProject上的例子

3.2. Synchronous Method & Thread

使用.net ThreadPool类的话,自己不能对Thread进行控制。而且此ThreadPool类有一些缺点(比如不适合于长时间执行某操作的场合)。这个时候我们可以直接创建Thread来实现异步,这个时候我们自己需要去做更多的工作——使用一个Thread去接收TCP连接,对于每个连接还需要开一个Thread去接收数据,另外还需要注意线程的同步、资源的释放和各种异常情况的处理;另外,性能也是一个必须重视的问题。

CodeProject上的例子

3.3. Synchronous Method & .Net ThreadPool

其实这么做和使用AsyncCallback Method实质上是一样的——都是使用了.Net ThreadPool。和Synchronous Method & Thread不同的就是不用自己去管理线程了,而把线程管理的复杂性交给ThreadPool类来做了,它为我们使用多线程提供了便利。而且,.Net ThreadPool充分利用CPU的时间片,提供了比较高的性能,也有人做了测试证明使用ThreadPool通常会获比直接创建和管理多个线程更为理想的性能。

3.4. Synchronous Method & Own ThreadPool

如果不想使用.Net ThreadPool类,那我们完全可以自己写一个ThreadPool.Net ThreadPool有一些缺点,其中主要的几个为:

1、  一个Process中只能有一个实例,它在各个AppDomain是共享的。ThreadPool只提供了静态方法,不仅我们自己添加进去的WorkItem使用这个Pool,而且.net framework中那些BeginXXXEndXXX之类的方法都会使用此Pool

2、  所支持的Callback不能有返回值。WaitCallback只能带一个object类型的参数,没有任何返回值。

3、  不能方便地修改最小和最大线程数。ThreadPool最大线程数为25个,有的情况下可能不够用的。如果要修改限制还颇费周折——需要使用COM

4、  不适合用在长期执行某任务的场合。我们常常需要做一个Service来提供不间断的服务(除非服务器down掉),但是使用ThreadPool并不合适。

我们自己写ThreadPool的话,可以使用Hashtable来存放Thread,再实现WorkItemWorkItemQueue这样的类来排队Work Item,使用WaitHandle来做同步。早已经有人因不满.net ThreadPool而编写了自己的ThreadPool,个人认为比较好的有SmartThreadPoolXYThreadPool

把对线程的管理交给ThreadPool后,我们就可以专注于通信方面的工作了。

4.   My Asynchronous Socket Lib

我实现的一个简单的TCP Asynchronous Socket Lib有如下特点:

1、  适合于局域网和Internet的网络环境。

2、  Server能接收无限量的client连接——当然受server资源的限制。

3、  Server会保持client的连接一段时间(timeout),以便于serverclient发送消息。这样可以保证即使client端在防火墙内,server也能给client发送消息。

4、  使用Length-leading的消息格式。通常区分TCP消息边界有三种方式:(1)发送固定长度的消息(2)使用Length-leading的消息(3)使用消息分隔符。

5、  对于server,接收到的消息存放到queue中,并触发事件。即使server down掉,消息也不丢失。

6、  对于server,待发送的消息也存入queue中,并触发事件。

7、  使用一个Thread Pool(不是.net framework提供的ThreadPool)来管理线程。分别有线程去做接收连接请求、检测连接是否可用和有无消息、接收消息和检查超时的工作。

8、  提供很多事件,比如OnServerStartOnNewClientOnReceiveMessageOnServerErrorOnServerStop等等。

9、  检查client连接。Client自己断开了连接后,在server端从socketAvailable属性并不能判断出来,可行的办法是使用Poll方法,然后看是否能peek到数据;或者接收数据的时候捕捉异常。就我自己实践的结果,使用Poll方法会有一些麻烦:我是将client连接进来的socket存放到Hashtable中的,但是调用了Poll方法后,会使此Hashtable被改变,从而导致迭代此Hashtable失败。CodeGuru上的例子介绍了使用异常的方法。

附codeguru的例子

Asynchronous Socket Programming in C#: Part II
An advanced C# socket program example with a single server and multiple simultanneous clients
Rating:

Jayan Nair (view profile)
October 6, 2005

Environment:  C#, Windows

Motivation for This Article

After the original article on Asynchronous Socket Programming in C# was published by CodeGuru, I received numerous responses from interested readers. Most of them asked for additional features that were missing in the original example. The original article was intended to show a simplistic example for asynchronous socket programming. To keep the simplicity of the original example, instead of modifying it, I am providing a more comprehensive example by adding the features requested by the readers.

Requested Features Added


(continued)

This example includes modifications to support the following features:

  1. How to support an unlimited number of clients
  2. How to find which client sent a particular message
  3. How to find which client sent a particular message
  4. How to reply or send messages to specific clients
  5. How to find when a particular client is disconnected
  6. How to get the list of all connected clients at any given time
  7. Are variables safe in AsyncCallback methods? What about thread synchronization? [Updated on 02/01/05]
How to support an unlimited number of clients
  • How to find which client sent a particular message
  • How to find which client sent a particular message
  • How to reply or send messages to specific clients
  • How to find when a particular client is disconnected
  • How to get the list of all connected clients at any given time
  • Are variables safe in AsyncCallback methods? What about thread synchronization? [Updated on 02/01/05]
  • Other Enhancements

    1. On the server and client code, the receive buffer size is increased to 1024 instead of a single byte for more efficiency.
    2. Cleanup code is added after a client is disconnected.

    Screen shot of Socket Server:

    Screen shot of Socket Client:

    How to Support an Unlimited Number of Clients

    This was an easy feature to add. In the original article, an array of Socket objects was used to store the references to the worker sockets. This is now modified to use an ArrayList as shown in the following code. (A HashTable also would have worked if you wanted to use a string instead of an index to track the connected clients.)

    Note: If you want to run your server for an infinite duration, there is the possibility of overflow of the integer value of the m_clientCount variable. In such scenarios, you may want to reconsider using this numbering scheme for clients. This example will still work on such scenarios, as long as you don't number your clients. But, this issue goes beyond the scope of this article.
    // An ArrayList is used to keep track of worker sockets that are
    // designed to communicate with each connected client
    private System.Collections.ArrayList m_workerSocketList =
       new System.Collections.ArrayList();
    // The following variable will keep track of the cumulative
    // total number of clients connected at any time
    private int m_clientCount = 0;
    

    How to Find Which Client Sent a Particular Message

    When multiple clients are connected, you may need to differentiate between the messages received from different clients. A

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值