分享

【用SocketAsyncEventArgs 池 线程构建服务器“推”】的源代码分析1

 fatcat916 2012-01-22

【用SocketAsyncEventArgs+池+线程构建服务器“推”】的源代码分析1

2009-07-24 10:01 by Creason New, 991 visits, 收藏, 编辑
      上一篇文章《用SocketAsyncEventArgs+池+线程构建服务器“推”》给大家展示了一下如何使用已封装好了的类库,这回再把源代码贴出来,大家讨论一下做为高性能的服务器“推”或是Socket服务器这个方案是否合适,或是有什么改进的地方。首先谢谢您的阅读。
      还是先把类图贴出来,免得大家还得去上一篇文章里看类图:
类图
       再依次贴每个类的代码和说明:
      1.MySocketAsyncEventArgs类:这个类的主要目的就是继承SocketAsyncEventArgs类,让它作为封装连接的类代替传统上的SocketAsyncEventArgs类,当然它扩展出UID和Property两个属性(看类图),UID是这个对象对应的用户的标识,比如张三的连接对象Asocket:MySocketAsyncEventArgs的UID属性就是张三。Property是这个对象是用来读数据的还是用来写数据的,值为"Receive"or"Send"。当然每个用户有两个这样的对象分别是用于读和写。接下来贴代码:

    
internal sealed class MySocketAsyncEventArgs : SocketAsyncEventArgs
    {
        
internal string UID;
        
private string Property;
        
internal MySocketAsyncEventArgs(string property)
        {
            
this.Property = property;
        }
    }
     2.SocketAsyncEventArgsWithId类:这个类也很简单,就是保存每一个用户连接的最小单元。大家还是看代码吧,很明了。

 1 
 2     internal sealed class SocketAsyncEventArgsWithId
 3     {
 4         private string uid;
 5         internal string UID
 6         {
 7             get { return uid; }
 8             set {
 9                 uid = value;
10                 ReceiveSAEA.UID = value;
11                 SendSAEA.UID = value;
12             }
13         }
14         internal MySocketAsyncEventArgs ReceiveSAEA;
15         internal MySocketAsyncEventArgs SendSAEA;
16 
17         internal SocketAsyncEventArgsWithId()
18         {
19             ReceiveSAEA = new MySocketAsyncEventArgs("Receive");
20             SendSAEA = new MySocketAsyncEventArgs("Send");
21         }
22 
      3.SocketAsyncEventArgsPool类,这就是我们的连接池了,它里面保存着n个用于连接的SocketAsyncEventArgsWithId对象,n=并发连接数,作为“池”,我用了Stak<SocketAsyncEventArgsWithId>,这样用到了就Pop,不用了就Push,另外还有一个IDictionary<string, SocketAsyncEventArgsWithId>,这个就是用来存忙碌的连接的,也就是说用户连接了就把SocketAsyncEventArgsWithId连接对象从Stak里Pop出然后Add进IDictionary,如果用户不用了这个连接就Push进Stak and 从IDictionary里Remove。为什么还用弄个IDictionary呢?是为了从诸多的以及正在使用的连接里面找出UID为xxx的那个连接,主要是为了FindByUID这个方法。下面看代码:

 1 
 2     internal sealed class SocketAsyncEventArgsPool
 3     {
 4         internal Stack<SocketAsyncEventArgsWithId> pool;
 5         internal IDictionary<string, SocketAsyncEventArgsWithId> busypool;
 6         internal SocketAsyncEventArgsPool(Int32 capacity)
 7         {
 8             this.pool = new Stack<SocketAsyncEventArgsWithId>(capacity);
 9             this.busypool = new Dictionary<string, SocketAsyncEventArgsWithId>();
10         }
11         internal Int32 Count
12         {
13             get { return this.pool.Count; }
14         }
15         internal SocketAsyncEventArgsWithId Pop()
16         {
17             lock (this.pool)
18             {
19                 SocketAsyncEventArgsWithId argsid = this.pool.Pop();
20                 return argsid;
21             }
22         }
23         internal void Push(SocketAsyncEventArgsWithId item)
24         {
25             if (item == null)
26                 throw new ArgumentNullException("SocketAsyncEventArgs对象为空");
27             lock (this.pool)
28             {
29                 if(busypool.Keys.Count!=0)
30                     if(busypool.Keys.Contains(item.UID))
31                         busypool.Remove(item.UID);
32                 item.UID = null;
33                 this.pool.Push(item);
34             }
35         }
36         internal SocketAsyncEventArgsWithId FindByUID(string uid)
37         {
38             SocketAsyncEventArgsWithId SI = null;
39             foreach (string key in busypool.Keys)
40             {
41                 if (key == uid)
42                 {
43                     SI = busypool[uid];
44                     break;
45                 }
46             }
47             return SI;
48         }
49 
      4.BufferManager类,这个就是为每一个SocketAsyncEventArgs对象分配缓冲区的,当然只给读的分配缓冲区,写的就不必了。该类也很简单,代码比口舌更好。

 1 
 2     internal sealed class BufferManager
 3     {
 4         private Byte[] buffer;
 5         private Int32 bufferSize;
 6         private Int32 numSize;
 7         private Int32 currentIndex;
 8         private Stack<Int32> freeIndexPool;
 9 
10         internal BufferManager(Int32 numSize, Int32 bufferSize)//分别为缓冲区的总数和分配给每一个连接的大小
11         {
12             this.bufferSize = bufferSize;
13             this.numSize = numSize;
14             this.currentIndex = 0;
15             this.freeIndexPool = new Stack<Int32>();
16         }
17 
18         internal void FreeBuffer(SocketAsyncEventArgs args)
19         {
20             this.freeIndexPool.Push(args.Offset);
21             args.SetBuffer(null00);
22         }
23         internal void InitBuffer()
24         {
25             this.buffer = new Byte[this.numSize];
26         }
27         internal Boolean SetBuffer(SocketAsyncEventArgs args)
28         {
29             if (this.freeIndexPool.Count > 0)
30             {
31                 args.SetBuffer(this.buffer, this.freeIndexPool.Pop(), this.bufferSize);
32             }
33             else 
34             {
35                 if ((this.numSize - this.bufferSize) < this.currentIndex)
36                 {
37                     return false;
38                 }
39                 args.SetBuffer(this.buffer, this.currentIndex, this.bufferSize);
40                 this.currentIndex += this.bufferSize;
41             }
42             return true;
43         }
44 
      5.RequestHandler类,这个类就是处理发送和接受的字符串的,大家知道Socket经常会出现这么一种情况,client先后发送A,B两条信息,有可能A和B的一半先到server端了,原因是缓冲区和tcp的机制,而剩下的一半B就要等下一次发送,那我们作为接收的是不是很痛苦啊,所以做了一个处理,就是在发送的字符串前加上”长度头“,比如我们发送"hello world",那么真正发送的是"[length=11]hello world",接收到这条信息之后就要检查信息的长度和头是否一致,否则等待下一次信息到来再处理接收未完成的信息。如果还不明白就看这篇文章

 1 
 2     public class RequestHandler
 3     {
 4         private string temp = string.Empty;
 5         public string[] GetActualString(string input)
 6         {
 7             return GetActualString(input, null);
 8         }
 9         private string[] GetActualString(string input, List<string> outputList)
10         {
11             if (outputList == null)
12                 outputList = new List<string>();
13             if (!String.IsNullOrEmpty(temp))
14                 input = temp + input;
15             string output = "";
16             string pattern = @"(?<=^\[length=)(\d+)(?=\])";
17             int length;
18             if (Regex.IsMatch(input, pattern))
19             {
20                 Match m = Regex.Match(input, pattern);
21                 length = Convert.ToInt32(m.Groups[0].Value);
22                 int startIndex = input.IndexOf(']'+ 1;
23                 output = input.Substring(startIndex);
24                 if (output.Length == length)
25                 {
26                     outputList.Add(output);
27                     temp = "";
28                 }
29                 else if (output.Length < length)
30                 {
31                     temp = input;
32                 }
33                 else if (output.Length > length)
34                 {
35                     output = output.Substring(0, length);
36                     outputList.Add(output);
37                     temp = "";
38                     input = input.Substring(startIndex + length);
39                     GetActualString(input, outputList);
40                 }
41             }
42             else
43             {
44                 temp = input;
45             }
46             return outputList.ToArray();
47         }
48 
      至于SocketListener和SocketClient由于代码比较多,放到下次吧。

      相关文章:  http://www.cnblogs.com/jeriffe/articles/1407603.html     

                       http://www.cnblogs.com/chuncn/archive/2009/06/22/1508018.html

                       http://www.cnblogs.com/dabing/archive/2009/07/10/1520586.html

                       http://www.cnblogs.com/JimmyZhang/archive/2008/09/16/1291854.html

      附上源代码:下载源代码    http://files.cnblogs.com/niuchenglei/SocketLib.zip

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约