1. 程式人生 > >C#SocketAsyncEventArgs實現高效能多併發TCPSocket通訊 (客戶端實現)

C#SocketAsyncEventArgs實現高效能多併發TCPSocket通訊 (客戶端實現)

http://freshflower.iteye.com/blog/2285286

  上一篇講了伺服器端的實現, 這一篇就是客戶端的實現.

     與伺服器不同的是客戶端的實現需要多個SocketAsyncEventArgs共同協作,至少需要兩個:接收的只需要一個,傳送的需要一個,也可以多個,這在多執行緒中尤為重要,接下來說明。

     客戶端一般需要資料的時候,就要發起請求,在多執行緒環境中,請求伺服器一般不希望列隊等候,這樣會大大拖慢程式的處理。如果傳送資料包的SocketAsyncEventArgs只有一個,且當他正在工作的時候, 下一個請求也來訪問,這時會丟擲異常, 提示當前的套接字正在工作, 所以這不是我們願意看到, 唯有增加

SocketAsyncEventArgs物件來解決。 那麼接下來的問題就是我怎麼知道當前的SocketAsyncEventArgs物件是否正在工作呢. 很簡單,我們新建一個MySocketEventArgs類來繼承它。

C#程式碼  收藏程式碼
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Net.Sockets;  
  5. using System.Text;  
  6. namespace Plates.Client.Net  
  7. {  
  8.     class MySocketEventArgs : SocketAsyncEventArgs  
  9.     {  
  10.         /// <summary>
  11.         /// 標識,只是一個編號而已
  12.         /// </summary>
  13.         publicint ArgsTag { getset; }  
  14.         /// <summary>
  15.         /// 設定/獲取使用狀態
  16.         /// </summary>
  17.         publicbool IsUsing { getset; }  
  18.     }  
  19. }  

     接下來,我們還需要BufferManager類,這個類已經在服務端貼出來了,與服務端是一樣的, 再貼一次:

C#程式碼  收藏程式碼
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Net.Sockets;  
  5. using System.Text;  
  6. using System.Threading.Tasks;  
  7. namespace Plates.Client.Net  
  8. {  
  9.     class BufferManager  
  10.     {  
  11.         int m_numBytes;                 // the total number of bytes controlled by the buffer pool
  12.         byte[] m_buffer;                // the underlying byte array maintained by the Buffer Manager
  13.         Stack<int> m_freeIndexPool;     // 
  14.         int m_currentIndex;  
  15.         int m_bufferSize;  
  16.         public BufferManager(int totalBytes, int bufferSize)  
  17.         {  
  18.             m_numBytes = totalBytes;  
  19.             m_currentIndex = 0;  
  20.             m_bufferSize = bufferSize;  
  21.             m_freeIndexPool = new Stack<int>();  
  22.         }  
  23.         // Allocates buffer space used by the buffer pool
  24.         publicvoid InitBuffer()  
  25.         {  
  26.             // create one big large buffer and divide that 
  27.             // out to each SocketAsyncEventArg object
  28.             m_buffer = newbyte[m_numBytes];  
  29.         }  
  30.         // Assigns a buffer from the buffer pool to the 
  31.         // specified SocketAsyncEventArgs object
  32.         //
  33.         // <returns>true if the buffer was successfully set, else false</returns>
  34.         publicbool SetBuffer(SocketAsyncEventArgs args)  
  35.         {  
  36.             if (m_freeIndexPool.Count > 0)  
  37.             {  
  38.                 args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);  
  39.             }  
  40.             else
  41.             {  
  42.                 if ((m_numBytes - m_bufferSize) < m_currentIndex)  
  43.                 {  
  44.                     returnfalse;  
  45.                 }  
  46.                 args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);  
  47.                 m_currentIndex += m_bufferSize;  
  48.             }  
  49.             returntrue;  
  50.         }  
  51.         // Removes the buffer from a SocketAsyncEventArg object.  
  52.         // This frees the buffer back to the buffer pool
  53.         publicvoid FreeBuffer(SocketAsyncEventArgs args)  
  54.         {  
  55.             m_freeIndexPool.Push(args.Offset);  
  56.             args.SetBuffer(null, 0, 0);  
  57.         }  
  58.     }  
  59. }  

     接下來是重點實現了,別的不多說,看程式碼:

C#程式碼  收藏程式碼
  1. using System;  
  2. using System.Collections;  
  3. using System.Collections.Generic;  
  4. using System.Linq;  
  5. using System.Net;  
  6. using System.Net.Sockets;  
  7. using System.Text;  
  8. using System.Threading;  
  9. using System.Threading.Tasks;  
  10. namespace Plates.Client.Net  
  11. {  
  12.     class SocketManager: IDisposable  
  13.     {  
  14.         privateconst Int32 BuffSize = 1024;  
  15.         // The socket used to send/receive messages.
  16.         private Socket clientSocket;  
  17.         // Flag for connected socket.
  18.         private Boolean connected = false;  
  19.         // Listener endpoint.
  20.         private IPEndPoint hostEndPoint;  
  21.         // Signals a connection.
  22.         privatestatic AutoResetEvent autoConnectEvent = new AutoResetEvent(false);  
  23.         BufferManager m_bufferManager;  
  24.         //定義接收資料的物件
  25.         List<byte> m_buffer;   
  26.         //傳送與接收的MySocketEventArgs變數定義.
  27.         private List<MySocketEventArgs> listArgs = new List<MySocketEventArgs>();  
  28.         private MySocketEventArgs receiveEventArgs = new MySocketEventArgs();  
  29.         int tagCount = 0;  
  30.         /// <summary>
  31.         /// 當前連線狀態
  32.         /// </summary>
  33.         publicbool Connected { get { return clientSocket != null && clientSocket.Connected; } }  
  34.         //伺服器主動發出資料受理委託及事件
  35.         publicdelegatevoid OnServerDataReceived(byte[] receiveBuff);  
  36.         publicevent OnServerDataReceived ServerDataHandler;  
  37.         //伺服器主動關閉連線委託及事件
  38.         publicdelegatevoid OnServerStop();  
  39.         publicevent OnServerStop ServerStopEvent;  
  40.         // Create an uninitialized client instance.
  41.         // To start the send/receive processing call the
  42.         // Connect method followed by SendReceive method.
  43.         internal SocketManager(String ip, Int32 port)  
  44.         {  
  45.             // Instantiates the endpoint and socket.
  46.             hostEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);  
  47.             clientSocket = new Socket(hostEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);  
  48.             m_bufferManager = new BufferManager(BuffSize * 2, BuffSize);  
  49.             m_buffer = new List<byte>();  
  50.         }  
  51.         /// <summary>
  52.         /// 連線到主機
  53.         /// </summary>
  54.         /// <returns>0.連線成功, 其他值失敗,參考SocketError的值列表</returns>
  55.         internal SocketError Connect()  
  56.         {  
  57.             SocketAsyncEventArgs connectArgs = new SocketAsyncEventArgs();  
  58.             connectArgs.UserToken = clientSocket;  
  59.             connectArgs.RemoteEndPoint = hostEndPoint;  
  60.             connectArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);  
  61.             clientSocket.ConnectAsync(connectArgs);  
  62.             autoConnectEvent.WaitOne(); //阻塞. 讓程式在這裡等待,直到連線響應後再返回連線結果
  63.             return connectArgs.SocketError;  
  64.         }  
  65.         /// Disconnect from the host.
  66.         internalvoid Disconnect()  
  67.         {  
  68.             clientSocket.Disconnect(false);  
  69.         }  
  70.         // Calback for connect operation
  71.         privatevoid OnConnect(object sender, SocketAsyncEventArgs e)  
  72.         {  
  73.             // Signals the end of connection.
  74.             autoConnectEvent.Set(); //釋放阻塞.
  75.             // Set the flag for socket connected.
  76.             connected = (e.SocketError == SocketError.Success);  
  77.             //如果連線成功,則初始化socketAsyncEventArgs
  78.             if (connected)   
  79.                 initArgs(e);  
  80.         }  
  81.         #region args
  82.         /// <summary>
  83.         /// 初始化收發引數
  84.         /// </summary>
  85.         /// <param name="e"></param>
  86.         privatevoid initArgs(SocketAsyncEventArgs e)  
  87.         {  
  88.             m_bufferManager.InitBuffer();  
  89.             //傳送引數
  90.             initSendArgs();  
  91.             //接收引數
  92.             receiveEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);  
  93.             receiveEventArgs.UserToken = e.UserToken;  
  94.             receiveEventArgs.ArgsTag = 0;  
  95.             m_bufferManager.SetBuffer(receiveEventArgs);  
  96.             //啟動接收,不管有沒有,一定得啟動.否則有資料來了也不知道.
  97.             if (!e.ConnectSocket.ReceiveAsync(receiveEventArgs))  
  98.                 ProcessReceive(receiveEventArgs);  
  99.         }  
  100.         /// <summary>
  101.         /// 初始化傳送引數MySocketEventArgs
  102.         /// </summary>
  103.         /// <returns></returns>
  104.         MySocketEventArgs initSendArgs()  
  105.         {  
  106.             MySocketEventArgs sendArg = new MySocketEventArgs();  
  107.             sendArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);  
  108.             sendArg.UserToken = clientSocket;  
  109.             sendArg.RemoteEndPoint = hostEndPoint;  
  110.             sendArg.IsUsing = false;  
  111.             Interlocked.Increment(ref tagCount);  
  112.             sendArg.ArgsTag = tagCount;  
  113.             lock (listArgs)  
  114.             {  
  115.                 listArgs.Add(sendArg);  
  116.             }  
  117.             return sendArg;  
  118.         }  
  119.         void IO_Completed(object sender, SocketAsyncEventArgs e)  
  120.         {  
  121.             MySocketEventArgs mys = (MySocketEventArgs)e;  
  122.             // determine which type of operation just completed and call the associated handler
  123.             switch (e.LastOperation)  
  124.             {  
  125.                 case SocketAsyncOperation.Receive:  
  126.                     ProcessReceive(e);  
  127.                     break;  
  128.                 case SocketAsyncOperation.Send:  
  129.                     mys.IsUsing = false//資料傳送已完成.狀態設為False
  130.                     ProcessSend(e);  
  131.                     break;  
  132.                 default:  
  133.                     thrownew ArgumentException("The last operation completed on the socket was not a receive or send");  
  134.             }  
  135.         }  
  136.         // This method is invoked when an asynchronous receive operation completes. 
  137.         // If the remote host closed the connection, then the socket is closed.  
  138.         // If data was received then the data is echoed back to the client.
  139.         //
  140.         privatevoid ProcessReceive(SocketAsyncEventArgs e)  
  141.         {  
  142.             try
  143.             {  
  144.                 // check if the remote host closed the connection
  145.                 Socket token = (Socket)e.UserToken;  
  146.                 if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)  
  147.                 {  
  148.                     //讀取資料
  149.                     byte[] data = newbyte[e.BytesTransferred];  
  150.                     Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);  
  151.                     lock (m_buffer)  
  152.                     {  
  153.                         m_buffer.AddRange(data);  
  154.                     }  
  155.                     do
  156.                     {  
  157.                         //注意: 這裡是需要和伺服器有協議的,我做了個簡單的協議,就是一個完整的包是包長(4位元組)+包資料,便於處理,當然你可以定義自己需要的; 
  158.                         //判斷包的長度,前面4個位元組.
  159.                         byte[] lenBytes = m_buffer.GetRange(0, 4).ToArray();  
  160.                         int packageLen = BitConverter.ToInt32(lenBytes, 0);  
  161.                         if (packageLen <= m_buffer.Count - 4)  
  162.                         {  
  163.                             //包夠長時,則提取出來,交給後面的程式去處理
  164.                             byte[] rev = m_buffer.GetRange(4, packageLen).ToArray();  
  165.                             //從資料池中移除這組資料,為什麼要lock,你懂的
  166.                             lock (m_buffer)  
  167.                             {  
  168.                                 m_buffer.RemoveRange(0, packageLen + 4);  
  169.                             }  
  170.                             //將資料包交給前臺去處理
  171.                             DoReceiveEvent(rev);  
  172.                         }  
  173.                         else
  174.                         {   //長度不夠,還得繼續接收,需要跳出迴圈
  175.                             break;  
  176.                         }  
  177.                     } while (m_buffer.Count > 4);  
  178.                     //注意:你一定會問,這裡為什麼要用do-while迴圈?   
  179.                     //如果當服務端傳送大資料流的時候,e.BytesTransferred的大小就會比服務端傳送過來的完整包要小,  
  180.                     //需要分多次接收.所以收到包的時候,先判斷包頭的大小.夠一個完整的包再處理.  
  181.                     //如果伺服器短時間內傳送多個小資料包時, 這裡可能會一次性把他們全收了.  
  182.                     //這樣如果沒有一個迴圈來控制,那麼只會處理第一個包,  
  183.                     //剩下的包全部留在m_buffer中了,只有等下一個資料包過來後,才會放出一個來.
  184.                     //繼續接收
  185.                     if (!token.ReceiveAsync(e))  
  186.                         this.ProcessReceive(e);  
  187.                 }  
  188.                 else
  189.                 {  
  190.                     ProcessError(e);  
  191.                 }  
  192.             }  
  193.             catch (Exception xe)  
  194.             {  
  195.                 Console.WriteLine(xe.Message);  
  196.             }  
  197.         }  
  198.         // This method is invoked when an asynchronous send operation completes.  
  199.         // The method issues another receive on the socket to read any additional 
  200.         // data sent from the client
  201.         //
  202.         // <param name="e"></param>
  203.         privatevoid ProcessSend(SocketAsyncEventArgs e)  
  204.         {  
  205.             if (e.SocketError != SocketError.Success)  
  206.             {   
  207.                 ProcessError(e);  
  208.             }  
  209.         }  
  210.         #endregion
  211.         #region read write
  212.         // Close socket in case of failure and throws
  213.         // a SockeException according to the SocketError.
  214.         privatevoid ProcessError(SocketAsyncEventArgs e)  
  215.         {  
  216.             Socket s = (Socket)e.UserToken;  
  217.             if (s.Connected)  
  218.             {  
  219.                 // close the socket associated with the client
  220.                 try
  221.                 {  
  222.                     s.Shutdown(SocketShutdown.Both);  
  223.                 }  
  224.                 catch (Exception)  
  225.                 {  
  226.                     // throws if client process has already closed
  227.                 }  
  228.                 finally
  229.                 {  
  230.                     if (s.Connected)  
  231.                     {  
  232.                         s.Close();  
  233.                     }  
  234.                     connected = false;  
  235.                 }  
  236.             }  
  237.             //這裡一定要記得把事件移走,如果不移走,當斷開伺服器後再次連線上,會造成多次事件觸發.
  238.             foreach (MySocketEventArgs arg in listArgs)  
  239.                 arg.Completed -= IO_Completed;  
  240.             receiveEventArgs.Completed -= IO_Completed;  
  241.             if (ServerStopEvent != null)  
  242.                 ServerStopEvent();  
  243.         }  
  244.         // Exchange a message with the host.
  245.         internalvoid Send(byte[] sendBuffer)  
  246.         {  
  247.             if (connected)  
  248.             {  
  249.                 //先對資料進行包裝,就是把包的大小作為頭加入,這必須與伺服器端的協議保持一致,否則造成伺服器無法處理資料.
  250.                 byte[] buff = newbyte[sendBuffer.Length + 4];  
  251.                 Array.Copy(BitConverter.GetBytes(sendBuffer.Length), buff, 4);  
  252.                 Array.Copy(sendBuffer, 0, buff, 4, sendBuffer.Length);  
  253.                 //查詢有沒有空閒的傳送MySocketEventArgs,有就直接拿來用,沒有就建立新的.So easy!
  254.                 MySocketEventArgs sendArgs = listArgs.Find(a => a.IsUsing == false);  
  255.                 if (sendArgs == null) {  
  256.                     sendArgs = initSendArgs();  
  257.                 }  
  258.                 lock (sendArgs) //要鎖定,不鎖定讓別的執行緒搶走了就不妙了.
  259.                 {  
  260.                     sendArgs.IsUsing = true;  
  261.                     sendArgs.SetBuffer(buff, 0, buff.Length);  
  262.                 }  
  263.                 clientSocket.SendAsync(sendArgs);  
  264.             }  
  265.             else
  266.             {  
  267.                 thrownew SocketException((Int32)SocketError.NotConnected);  
  268.             }  
  269.         }  
  270.         /// <summary>
  271.         /// 使用新程序通知事件回撥
  272.         /// </summary>
  273.         /// <param name="buff"></param>
  274.         privatevoid DoReceiveEvent(byte[] buff)  
  275.         {  
  276.             if (ServerDataHandler == nullreturn;  
  277.             //ServerDataHandler(buff); //可直接呼叫.
  278.             //但我更喜歡用新的執行緒,這樣不拖延接收新資料.
  279.             Thread thread = new Thread(new ParameterizedThreadStart((obj) =>  
  280.             {  
  281.                 ServerDataHandler((byte[])obj);  
  282.             }));  
  283.             thread.IsBackground = true;  
  284.             thread.Start(buff);  
  285.         }  
  286.         #endregion
  287.         #region IDisposable Members
  288.         // Disposes the instance of SocketClient.
  289.         publicvoid Dispose()  
  290.         {  
  291.             autoConnectEvent.Close();  
  292.             if (clientSocket.Connected)  
  293.             {  
  294.                 clientSocket.Close();  
  295.             }  
  296.         }  
  297.         #endregion
  298.     }  
  299. }  
 

 好了, 怎麼使用, 那是再簡單不過的事了, 當然連線同一個伺服器的同一埠, 這個類你只需要初始化一次就可以了, 不要建立多個, 這樣太浪費資源. 上面是定義了通訊的基礎類, 那麼接下來就是把相關的方法再包裝一下, 做成供前臺方便呼叫的含有靜態方法的類就OK了. 

C#程式碼  收藏程式碼
  1. using Newtonsoft.Json;  
  2. using Plates.Common;  
  3. using Plates.Common.Base;  
  4. using Plates.Common.Beans;  
  5. using RuncomLib.File;  
  6. using RuncomLib.Log;  
  7. using RuncomLib.Text;  
  8. using System;  
  9. using System.Collections.Generic;  
  10. using System.Linq;  
  11. using System.Net.Sockets;  
  12. using System.Security.Cryptography;  
  13. using System.Text;  
  14. using System.Threading;  
  15. using System.Timers;  
  16. namespace Plates.Client.Net  
  17. {  
  18.     class Request  
  19.     {  
  20.         //定義,最好定義成靜態的, 因為我們只需要一個就好
  21.         static SocketManager smanager = null;  
  22.         static UserInfoModel userInfo = null;  
  23.         //定義事件與委託
  24.         publicdelegatevoid ReceiveData(object message);  
  25.         publicdelegatevoid ServerClosed();  
  26.         publicstaticevent ReceiveData OnReceiveData;  
  27.         publicstaticevent ServerClosed OnServerClosed;  
  28.         /// <summary>
  29.         /// 心跳定時器
  30.         /// </summary>
  31.         static System.Timers.Timer heartTimer = null;  
  32.         /// <summary>
  33.         /// 心跳包
  34.         /// </summary>
  35.         static ApiResponse heartRes = null;  
  36.         /// <summary>
  37.         /// 判斷是否已連線
  38.         /// </summary>
  39.         publicstaticbool Connected  
  40.         {  
  41.             get { return smanager != null && smanager.Connected; }   
  42.         }  
  43.         /// <summary>
  44.         /// 已登入的使用者資訊
  45.         /// </summary>
  46.         publicstatic UserInfoModel UserInfo  
  47.         {  
  48.             get { return userInfo; }  
  49.         }  
  50.         #region 基本方法
  51.         /// <summary>
  52.         /// 連線到伺服器
  53.         /// </summary>
  54.         /// <returns></returns>
  55.         publicstatic SocketError Connect()  
  56.         {  
  57.             if (Connected) return SocketError.Success;  
  58.             //我這裡是讀取配置, 
  59.             string ip = Config.ReadConfigString("socket""server""");  
  60.             int port = Config.ReadConfigInt("socket""port", 13909);  
  61.             if (string.IsNullOrWhiteSpace(ip) || port <= 1000) return SocketError.Fault;  
  62.             //建立連線物件, 連線到伺服器
  63.             smanager = new SocketManager(ip, port);  
  64.             SocketError error = smanager.Connect();  
  65.             if (error == SocketError.Success){  
  66.                //連線成功後,就註冊事件. 最好在成功後再註冊.
  67.                 smanager.ServerDataHandler += OnReceivedServerData;  
  68.                 smanager.ServerStopEvent += OnServerStopEvent;  
  69.             }  
  70.             return error;  
  71.         }  
  72.         /// <summary>
  73.         /// 斷開連線
  74.         /// </summary>
  75.         publicstaticvoid Disconnect()  
  76.         {  
  77.             try
  78.             {  
  79.                 smanager.Disconnect();  
  80.             }  
  81.             catch (Exception) { }  
  82.         }  
  83.         /// <summary>
  84.         /// 傳送請求
  85.         /// </summary>
  86.         /// <param name="request"></param>
  87.         /// <returns></returns>
  88.         publicstaticbool Send(ApiResponse request)  
  89.         {  
  90.             return Send(JsonConvert.SerializeObject(request));  
  91.         }  
  92.         /// <summary>
  93.         /// 傳送訊息
  94.         /// </summary>
  95.         /// <param name="message">訊息實體</param>
  96.         /// <returns>True.已傳送; False.未傳送</returns>
  97.         publicstaticbool Send(string message)  
  98.         {  
  99.             if (!Connected) returnfalse;  
  100.             byte[] buff = Encoding.UTF8.GetBytes(message);  
  101.             //加密,根據自己的需要可以考慮把訊息加密
  102.             //buff = AESEncrypt.Encrypt(buff, m_aesKey);
  103.             smanager.Send(buff);  
  104.             returntrue;  
  105.         }  
  106.         /// <summary>
  107.         /// 傳送位元組流
  108.         /// </summary>
  109.         /// <param name="buff"></param>
  110.         /// <returns></returns>
  111.         staticbool Send(byte[] buff)  
  112.         {  
  113.             if (!Connected) returnfalse;  
  114.             smanager.Send(buff);  
  115.             returntrue;  
  116.         }  
  117.         /// <summary>
  118.         /// 接收訊息
  119.         /// </summary>
  120.         /// <param name="buff"></param>
  121.         privatestaticvoid OnReceivedServerData(byte[] buff)  
  122.         {  
  123.             //To do something
  124.             //你要處理的程式碼,可以實現把buff轉化成你具體的物件, 再傳給前臺
  125.             if (OnReceiveData != null)  
  126.                 OnReceiveData(buff);  
  127.         }  
  128.         /// <summary>
  129.         /// 伺服器已斷開
  130.         /// </summary>
  131.         privatestaticvoid OnServerStopEvent()  
  132.         {  
  133.             if (OnServerClosed != null)  
  134.                 OnServerClosed();  
  135.         }  
  136.         #endregion
  137.         #region 心跳包
  138.         //心跳包也是很重要的,看自己的需要了, 我只定義出來, 你自己找個地方去呼叫吧
  139.         /// <summary>
  140.         /// 開啟心跳
  141.         /// </summary>
  142.         privatestaticvoid StartHeartbeat()  
  143.         {  
  144.             if (heartTimer == null)  
  145.             {  
  146.                 heartTimer = new System.Timers.Timer();  
  147.                 heartTimer.Elapsed += TimeElapsed;  
  148.             }  
  149.             heartTimer.AutoReset = true;     //迴圈執行
  150.             heartTimer.Interval = 30 * 1000; //每30秒執行一次
  151.             heartTimer.Enabled = true;  
  152.             heartTimer.Start();  
  153.             //初始化心跳包
  154.             heartRes = new ApiResponse((int)ApiCode.心跳);  
  155.             heartRes.data = new Dictionary<stringobject>();  
  156.             heartRes.data.Add("beat", Function.Base64Encode(userInfo.nickname + userInfo.userid + DateTime.Now.ToString("HH:mm:ss")));  
  157.         }  
  158.         /// <summary>
  159.         /// 定時執行
  160.         /// </summary>
  161.         /// <param name="source"></param>
  162.         /// <param name="e"></param>
  163.         staticvoid TimeElapsed(object source, ElapsedEventArgs e)  
  164.         {  
  165.             Request.Send(heartRes);  
  166.         }  
  167.         #endregion
  168.     }  
  169. }  

好了, 就這些, 所有的請求都是非同步進行的, 如果你想同步進行, 我也有實現過, 等有空了再貼上來.

如果你還沒有弄懂伺服器端, 請進入: