上一篇讲了服务器端的实现, 这一篇就是客户端的实现.
服务器实现参考:《C#如何利用SocketAsyncEventArgs实现高效能TCPSocket通信 (服务器实现)》
与服务器不同的是客户端的实现需要多个SocketAsyncEventArgs共同协作,至少需要两个:接收的只需要一个,发送的需要一个,也可以多个,这在多线程中尤为重要,接下来说明。
客户端一般需要数据的时候,就要发起请求,在多线程环境中,请求服务器一般不希望列队等候,这样会大大拖慢程序的处理。如果发送数据包的SocketAsyncEventArgs只有一个,且当他正在工作的时候, 下一个请求也来访问,这时会抛出异常, 提示当前的套接字正在工作, 所以这不是我们愿意看到, 唯有增加SocketAsyncEventArgs对象来解决。 那么接下来的问题就是我怎么知道当前的SocketAsyncEventArgs对象是否正在工作呢. 很简单,我们新建一个MySocketEventArgs类来继承它。
using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; namespace Plates.Client.Net { class MySocketEventArgs : SocketAsyncEventArgs { /// <summary> /// 标识,只是一个编号而已 /// </summary> public int ArgsTag { get; set; } /// <summary> /// 设置/获取使用状态 /// </summary> public bool IsUsing { get; set; } } }
接下来,我们还需要BufferManager类,这个类已经在服务端贴出来了,与服务端是一样的, 再贴一次:
using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace Plates.Client.Net { class BufferManager { int m_numBytes; // the total number of bytes controlled by the buffer pool byte[] m_buffer; // the underlying byte array maintained by the Buffer Manager Stack<int> m_freeIndexPool; // int m_currentIndex; int m_bufferSize; public BufferManager(int totalBytes, int bufferSize) { m_numBytes = totalBytes; m_currentIndex = 0; m_bufferSize = bufferSize; m_freeIndexPool = new Stack<int>(); } // Allocates buffer space used by the buffer pool public void InitBuffer() { // create one big large buffer and divide that // out to each SocketAsyncEventArg object m_buffer = new byte[m_numBytes]; } // Assigns a buffer from the buffer pool to the // specified SocketAsyncEventArgs object // // <returns>true if the buffer was successfully set, else false</returns> public bool SetBuffer(SocketAsyncEventArgs args) { if (m_freeIndexPool.Count > 0) { args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize); } else { if ((m_numBytes - m_bufferSize) < m_currentIndex) { return false; } args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize); m_currentIndex += m_bufferSize; } return true; } // Removes the buffer from a SocketAsyncEventArg object. // This frees the buffer back to the buffer pool public void FreeBuffer(SocketAsyncEventArgs args) { m_freeIndexPool.Push(args.Offset); args.SetBuffer(null, 0, 0); } } }
接下来是重点实现了,别的不多说,看代码:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Plates.Client.Net { class SocketManager: IDisposable { private const Int32 BuffSize = 1024; // The socket used to send/receive messages. private Socket clientSocket; // Flag for connected socket. private Boolean connected = false; // Listener endpoint. private IPEndPoint hostEndPoint; //