C# 中 Socket 进行数据接收的一点心得

在利用Socket 进行Tcp/Ip 编程的时候,免不了要进行数据的发送和接收,而数据的接收,用得最多的就是 Socket 的同步函数 Receive (或它的重载):
public int Receive (
    byte[] buffer,
    int offset,
    int size,
    SocketFlags socketFlags
)
或者是异步函数 BeginReceive( 或它的重载): public IAsyncResult BeginReceive (
    byte[] buffer,
    int offset,
    int size,
    SocketFlags socketFlags,
    AsyncCallback callback,
    Object state
)
这里,我想说的是异步函数 BeginReceive()。

使用了异步函数BeginReceive(),伴随而来的就是要定义一个 AsyncCallback 类型的函数(如下面取名为ReceiveEnd的函数),在这个函数里面,我们最少要做的事情时调用 Socket 的 EndReceive() 函数。 protected void ReceiveEnd(IAsyncResult ar){
    Socket client = (Socket)ar.AsyncState;
    int recvLen = client.EndReceive(ar);

    if ( recvLen > 0){        /**//* 接收到了数据 */
        client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveEnd), client);  /**//* 继续接收数据*/
    }
    else{
        client.Close();      
    }
}
在Msdn 2005 的 ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.NETDEVFX.v20.chs/cpref10/html/M_System_Net_Sockets_Socket_BeginReceive_3_2b547d4d.htm
基本上就是上面这个意思,但是现在问题来了:
如果我定义的 buffer 的长度为 10,但我发送的数据长度为12或者8,那么,函数就不会走到 else 的部分,而是阻塞在函数中的 BeginReceive()调用。

开始,我的想法是在发送的数据尾部增加一个特殊的字符(串),然后,在接收的时候判断有没有接收到这个字符(串)(我查阅了网上一些资料,还真有一些是这么做的!),但是,这种做法始终有一个毛病:那就是发送端发送的正文里面不能含有这个字符(串),否则,正文中在这个字符(串)后面的正文就会被丢掉。
所以,我想还是只能通过 Socket 自己的内部机制来实行,也就是想办法使程序走到 else 的部分。
经过测试:
在客户端发送完数据后,使用 Socket.Shutdown(SocketShutdown.Send ),或者Socket.Close()可以使程序走到 else 部分。
但是,这也带来了一个问题:
如果我定义的buffer 的长度为10,我发送的数据的长度也刚好为10,那么,函数在客户端不用Shutdown或Close的情况下,就已经走到了else 部分,然后 服务端的Socket 被 Close,等到客户端使用 Shutdown()或Close() 的时候,服务端的连接 Socket 已经是不可用了,而这个时候调用 EndReceive 操作,会引发异常。
所以,我认为在调用 EndReceive 前,先要判断 Socket 是否可用。(这个结论尚未测试)

同时,我测试将Socket.Shutdown(SocketShutdown.Send ),改为Socket.Shutdown(SocketShutdown.Receive ),并不能引发函数走到 else 部分,所以,我认为Socket 的Shutdown(SocketShutdown.Send )和Close()会引发客户端与服务端的一次数据传输。