Socket编程,对一些函数接口的认识我觉得很多人都不够深入,比如:Socket.Send,Socket.Receive,Socket.BeginReceive,Socket.BeginSend等函数做一些理解肤浅,导致写出的程序在外网的环境下,根本不能正常运行。网上很多列程都是粗粗的说了一下下,根本没有探讨和深入这些问题,导致很多初学者,被辱歧途(误人子弟啊)。今天闲来无聊,谢谢自己的经验给大家分享下吧!
Socket.Send是一个同步阻塞函数,表是向网络套借口发送数据,原型是:int Send(byte[] buffer, int offset, int size, SocketFlags socketFlags);这里面要注意返回值哦!很多初级程序员,以为只要调用这个函数,发送数据的事情就万事大吉了,这是因为在编程环境中,都是本机或100M内网,网络速度足够快,一般99%不会出现问题。但如果你到Internet环境中的时候,那就漏洞百出了。
这里首先要注意,此函数返回的是已经成功发送的字节数,你缓冲区的Buffer如果大于此返回值,这样数据就没有发送完毕。如果你要包装一个函数,一定要发送完你的Buffuer中的数据,那你建以你采用下面的方案:
public bool MySendData(const byte[] Buffer, Socket theSocket)
{
int SendPos = 0;//缓冲区发送的开始位置
int OneDataSendLen = 0;//每次发送的数量
while (SendPos < Buffer.Length)//发送完毕
{
OneDataSendLen = theSocket.Send(Buffer, SendPos, Buffer.Length - SendPos, SocketFlags.None);
SendPos += OneDataSendLen;//位置往后移动
}
return true;
}
同样的道理,Receive函数的包装方式也得调整,如果你接受一个固定大小的数据包,包装方式如下:
public byte[] MyReceive(const int PackLenth,Socket theSocket)
{
byte[] Buff = new bytePackLenth
int RecPos=0;
while (RecPos < PackLenth)//未接受完毕,继续接收
{
OneDataSendLen = theSocket.Receive(CmdBuff, RecPos, CmdLength - RecPos, SocketFlags.None);
RecPos += OneDataSendLen;
if (RecPos >= PackLenth)
break;
}
return Buff;
}
上面的Send和Receive是同步阻塞方法,一般在为了设计效率问题和灵活性,在做大规模高效的通信软件的情况下,一般是不会采用的,所以大家一般要用到一步方法BeginSend,BeginReceive方法。
先看看他们的原型:
public IAsyncResult BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state);
其它函数都差不多,关键多了一个AsyncCallback和State参数,AsyncCallback这表示此次发送完成后(注意是此次完成,不一定是所有数据已发送完成),需要执行的回调函数,其类 型为:void AsyncCallback(IAsyncResult ar);
另外还有一个Object 类型的State参数,这个参数用传送发送参数,并最终传递到AsyncCallback函数中,为此我们构建一个对象,
public class TDatagram
{
public int OffSet = 0;
public int OneLength = 1024 *8;//2
public int Length;
public byte[] Buffer;
public Socket TheSocket;
public bool IsObj;
}
//异步发送数据
public bool AsnySend( byte[] Buffer, Socket TheSocket)
{
TDatagram Datagram = new TDatagram();
Datagram.OffSet = 0;
Datagram.Length = Buffer.Length;
Datagram.Buffer = Buffer;
Datagram.TheSocket = TheSocket;
TheSocket.BeginSend(Buffer, 0,Buffer.Length, SocketFlags.None, new AsyncCallback(CallBackSendData), Datagram);
}
// 回调函数需要执行EndSend();
private void CallBackSendData(IAsyncResult IAsync)
{
TDatagram Datagram = (TDatagram)(IAsync.AsyncState);
Datagram.OffSet += Datagram.TheSocket.EndSend(IAsync);//回调结束此次发送,返回此次发送的字节数
if (Datagram.OffSet < Datagram.Buffer.Length)//没有发完,继续发送
{
Datagram.TheSocket.BeginSend(Datagram.Buffer, Datagram.OffSet, Datagram.Buffer.Length - Datagram.OffSet, SocketFlags.None, new AsyncCallback (CallBackSendData), Datagram);
}
}
就是说如果本次还没有发送完毕,需要继续调用发送方法,如果网络速度很慢,还可能出现反复调用发送方法。
至于异步接受方法的实现,大家可以参考发送的方法了,在此我不再做描述了。
本人曾经给海外朋友设计了一个大型高并发的实时赔率传送软件,基本就是采用上面的思路,实现了不同地区的数完整传输,没有出过差错,可以说是经过检验的。本文希望对大家设计自己专用的通信程序的时候做参考之用。如果一知半解,想当然,你的程序肯定漏洞百出!