socket 异步接收大数据包问题

Korny 2004-10-23 03:11:24
问题描述:在TCP socket 异步接收中,假设客户端向服务器发送一个大数据包,服务器必须分次用beginreceive来接收,有两个方试可以判断一个包是否已经接收完整,一是在流里先写入包的大小,二是在流里写入结束标志符。我现在采用第1种情况,但出现错误,提示是序列化时,我估计是包没有收完整。下面我贴出关键代码,请大家分析一下,是否正确? 以及如何解决服务器异步接收大包的情况?

//单个用户的socket
public class StateObject
{
public bool connected = false; // ID received flag
public Socket workSocket = null; // Client socket.
public Socket partnerSocket = null; // Partner socket.
public const int BufferSize = 4; // Size of receive buffer.
public byte[] buffer = new byte[BufferSize];// Receive buffer.
public StringBuilder sb = new StringBuilder();//Received data String.
public string id = String.Empty; // Host or conversation ID
public DateTime TimeStamp;

/*zsb*/
public byte[] MessBuffer;
public bool AllDataReceived=true;
public int DataSize;
public int DataLeft;
public int DataCurrentTotal;
} 


//服务器接收数据
protected void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;

// Retrieve the state object and the handler socket
// from the async state object.
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.workSocket;

try
{
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);

if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
/*zsb*/
Console.WriteLine("这次实际收到:"+bytesRead.ToString());

if(state.AllDataReceived) //上条数据全部收到,意味着新数据的开始
{

//recv=handler.Receive(datasize,0,4,0);
int size=BitConverter.ToInt32(state.buffer,0);

Console.WriteLine("消息大小:"+size.ToString());
state.DataSize=size;
state.MessBuffer=new byte[state.DataSize];
state.DataCurrentTotal=0;
state.DataLeft=size;
state.AllDataReceived=false;


state.DataCurrentTotal+=bytesRead;
state.DataLeft-=bytesRead;

//去收
handler.BeginReceive(state.MessBuffer, state.DataCurrentTotal, state.DataLeft, 0, new AsyncCallback(this.ReadCallback), state);

}
else
{
state.DataCurrentTotal+=bytesRead;
state.DataLeft-=bytesRead;
}

if(state.DataCurrentTotal>=state.DataSize)//数据已经完整
{
Console.WriteLine("数据已经完整");

state.AllDataReceived=true;

SerialMessage sm=new SerialMessage();
sm=(SerialMessage)OperMess.DecodingMessage(state.MessBuffer,0,state.MessBuffer.Length);

if(sm.ExecAction=="USER_LOGIN") //这里抢先注册,此时有sokcet
{
this.RegisterToUserHT_USERLOING(sm, state);
}

else
{

MessageQueue.Enqueue(sm);

}


// Console.WriteLine("去收前4个字节");
// handler.BeginReceive(state.buffer, 0, 4, 0,
// new AsyncCallback(this.ReadCallback), state);


}
else
{
Console.WriteLine("再去收");
handler.BeginReceive(state.MessBuffer, state.DataCurrentTotal, state.DataLeft, 0, new AsyncCallback(this.ReadCallback), state);

}




}//bytesRead >0
else
{ // Disconnected
this.RemoveUserFromState(state);
}
}
catch (System.Net.Sockets.SocketException es)
{
this.RemoveUserFromState(state);
if (es.ErrorCode != 64)
{
Console.WriteLine( string.Format("ReadCallback Socket Exception: {0}, {1}.", es.ErrorCode, es.ToString()));
}
}
catch (Exception e)
{
this.RemoveUserFromState(state);
if (e.GetType().FullName != "System.ObjectDisposedException")
{
Console.WriteLine(string.Format("ReadCallback Exception: {0}.", e.ToString()));
}
}




}

...全文
646 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
Korny 2004-10-25
  • 打赏
  • 举报
回复
我想如果不用指定长度的话还是比较麻烦而且效率不高

假设我们的设置的数据缓冲区大小为1024字节,对于流来讲,你这次收了1024其中在此700处有个结束标志,那么这本有效消息则是以前所有接收的数据+本次1024的前700数据的组合。那么下条有效数据则是从刚才的700后开始接收(此1024-700)已经被上次receive接收。必须再放到我们自己的”有效消息“的byte[]里。再去用receive继续接收1024。而且用标志的情况下,每次都必须去byte[]里的数据进行标志比较判断,影响效率,所以感觉还是用大小来处理,对于流来讲用偏移量来处理还是非常合适的.
schollc 2004-10-25
  • 打赏
  • 举报
回复
超过1024字节的包,发送端会自动截断,然后发送。接收端则不知道要自己分批次来收。
所以,建议使用你说的第2种,告诉接收端什么时候开始收,这时可以打开一个文件流,什么时候是中间的正文,这时候可以用append方式打开刚才文件流,什么时候结束,这是可以什么都不用做了,也就是当然你可以做一些其它的东西,比如把刚才的文件流的名字清空等等。

110,545

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧