解决方案 »
- 加100分了第二次:如何解webBrowser中的html保存为TIF文件
- vs2005 安装包制作 如何取得每个按钮事件
- C# 别人写好的程序 打包文件有了 修改后怎样打包
- xml的节点删除问题!!!急!!!
- ListView 第一行标题栏的字体大小和行高怎样设置呢?
- label控件怎么让他的背景为透明的呢
- 问个生成dll的问题
- 如何进行这个数据转换?懂的请进
- 再线等待!!急windows和web数据交换?????
- 如何捕获ListView的scroll bar滚动消息
- C# xp运行正常,win7 win8出现无法加载DLL,动态链接库初始化例程失败
- VS2010为什么没有局部变量提升为参数这个菜单选项
http://q.cnblogs.com/q/31971/
说“TcpClient NetworkStream 是基于流的 ”
要想接全要用BeginRead,可是BeginRead该怎么用?
接收数据后,首先进行数据校验,读取长度,
根据长度进行后续接收处理。
lz 提到接收中出现多余数据,就只发送的数据发送过来的
数据长度不定,而接收按定长处理导致。
再差的网络,包头几个字节还是能收到的。
如果包头校验失败,那继续接收也无意义,
可以断开该socket,让客户端重新连接。2.接收数据后,不要直接在该线程中进行数据解析处理。
需要考虑将数据放入队列或者其他容器,让接收动作立即返回。
否则一旦数据量较大,就会出现接收被阻塞的情况。
第二,不光要用异步还要用递归,因为你不一定知道发送来的消息的大小,还有一个原因是用递归不用考虑时间问题,什么时候数据接收完成了,什么时候才停止接收。如果你不用递归,你必须有足够的自信你的接收端不会漏掉每一个字节的数据。从windows消息循环的机制来探讨,但是由于各种原因会使得你服务器的消息循环速度不可能与每个客户端消息循环速度一致,如果你用同步方法,你不可能保证不会漏掉至少一个字节的数据。
msdn 上的关于通讯的例子,大多都是有 bug 的。那些例子在单机或者局域网(而且网络很好没有什么干扰),传送内容极短,并且并发数太低时不容易测试出来。buffer的大小,设置成100也可以(其实设置成1也可以),或者设置成 819200 也可以,而且大的buffer会比小的快。不论把buffer大小设置成100还是100万,都可以接收1万多个字节。但是都不能保证一次Read接收1万字节数据。不管你设置多大,你接收的数据都可能是对方的底层机制分包、粘包的结果,因此绝不能胡乱执行你的“//do sth”这种代码,因为一次 Read 完毕之后,可能你读取到119个字节,而其实对方发送的是200个字节的一个消息,你需要再执行一次Read(这一次readSize值为1)之后才能把两次收到的消息进行“do”。我在以前写过一个简单的示例:http://bbs.csdn.net/topics/390930620
虽然那只是针对“串口”的例子,但是重点是在于读取消息的预处理操作,你可以看看其流程。绝不是读取到一些字节就以为可以“do sth"了,你需要等到消息结束。也就是说,你要知道对方(或者你)制定的信令协议标准。例如,当我们以tcp方式来接收http协议的消息时,http协议规定了连续两个换行回车(四个字节)是消息结束符号。至于说“可是一样接不全,中间好像掺杂了一些没用的数据”,那就是你的客户端程序的bug问题了,它(如果有并发情况)没有能够一次性send数据,而是多次send且次序搞乱,以错误的顺序掺杂着你所谓“没用的数据”,这是你们自己搞的。
private static void RecvServerMsgThread()
{
while (m_ConnectSocket.Connected)
{ if (!m_Reader.CanRead)//DataAvailable
{
continue;
}
else
{
try
{
if (m_Reader.DataAvailable)//首次读,读包头和Md5
{
byte[] bytes = new byte[m_ConnectSocket.ReceiveBufferSize];
byte[] totalBytes = new byte[0];//C#神器,byte[]会自动延长
int readSize = m_Reader.Read(bytes, 0, (int)m_ConnectSocket.ReceiveBufferSize);
if (readSize > 0)//说明是有读到数据的
{
totalBytes = totalBytes.Concat(bytes).ToArray();//C#神器,byte[]会自动延长
byte[] md5 = new byte[32];
Array.Copy(totalBytes, 0, md5, 0, 32);
if (Global.BytesCompare(md5, GlobalDefine.KEY))//是我的包,开始循环读
{
byte[] packSizeBuffer = new byte[4];//先读整个包的大小(不包含MD5)
Array.Copy(totalBytes, 32, packSizeBuffer, 0, 4);//取出整个包的大小
int packSize = Global.BytesToInt(packSizeBuffer); int currentSize = readSize - 32 - packSizeBuffer.Length;//标记整个包有多少,默认减掉包头Md5+PackSize
while (m_Reader.DataAvailable || currentSize < packSize)
{
readSize = m_Reader.Read(bytes, 0, (int)m_ConnectSocket.ReceiveBufferSize);
if (readSize > 0)
{
currentSize += readSize;//读取成功,进行叠加
totalBytes = totalBytes.Concat(bytes).ToArray();//将读取的内容添加到容器后面
}
}
//读取完了,开始获取数据
try
{
byte[] data = new byte[packSize];
Array.Copy(totalBytes, 36, data, 0, packSize);//获取数据
DataThread.Star(data);
}
catch (Exception e)
{ }
}
}
}
}
catch (Exception e)
{
Global.ShowMessage(e.Message);
}
}
}
}上面是我客户端读消息的代码。Socket包的组成为:Md5标记(32,标记我的消息) + 数据长度 (4,我的数据) + 数据 (长度未知,包含在前面数据长度内)接收的时候先预读缓冲区的内容,如果读取成功,取出前32位判断是否为我的包,是的话在读取数据长度,然后在循环中依次读取数据。最后,将读取的数据开启数据处理线程,交由数据处理线程处理。
public static ManualResetEvent allDone = new ManualResetEvent(false);
private static byte[] bytes = new byte[m_ConnectSocket.ReceiveBufferSize];
private static byte[] totalBytes = new byte[0];//C#神器,byte[]会自动延长
public class State
{
public NetworkStream ns;
public byte[] buffer;
public byte[] result;
}
public static void myReadCallBack(IAsyncResult iar)
{
State state = (State)iar.AsyncState;
NetworkStream myNetworkStream = state.ns;
int numberOfBytesRead;
numberOfBytesRead = myNetworkStream.EndRead(iar);
if (numberOfBytesRead > 0)
{
totalBytes = totalBytes.Concat(state.buffer).ToArray();
//接收到的消息长度可能大于缓冲区总大小,反复循环直到读完为止
while (myNetworkStream.DataAvailable)
{
myNetworkStream.BeginRead(state.buffer, 0, state.buffer.Length, new AsyncCallback(myReadCallBack), state);
}
}
else
{
//执行这里我认为数据已经全部接收完毕
//开始执行如11楼的处理数据代码
//也就是从 state.buffer 中取出包头判断是否我的包
//然后提取数据长度,根据数据长度提取数据,将数据交由数据处理线程进行处理
allDone.Set();
}
} //线程接收代码如下
while (m_Reader.DataAvailable)
{
State state = new State();
state.ns = m_Reader;
state.buffer = new byte[m_ConnectSocket.ReceiveBufferSize]; ;
state.result = new byte[0];
totalBytes = new byte[0];
m_Reader.BeginRead(state.buffer, 0, state.buffer.Length, myReadCallBack, state);
allDone.WaitOne();
}
使用异步的BeginRead,但是代码没有实现我想要的结果。其实对于BeginRead很混乱,希望各位大牛能帮我理清思路。
因为网上关于BeginRead的代码真是惨不忍睹,全是代码片段,全是无脑复制,好一点的在网络环境好的情况下还能接收成功。
但更多的根本没用
你好,就我个人搜集资料来看,个人是赞同你的说法的,那么请问异步的BeginRead有没有示例呢?因为我根据自己网上搜索的写了运行结果并不是我想要的。代码在13 楼
这位兄弟所言甚是,那么能不能提供一些异步的示例给小弟参考呢?
你好,之前忘记CSDN自己最多只能连续三楼,所以代码传上去之后发现已经连盖三楼了。。此前搜C# Socket方面,很多CSDN都有看到你的回答,先谢谢了。你说的我不太懂,最坏情况下我也有想过将整个Socket包拆分,每个小包里面都有固定的开头或者结束标记,然后接受判断是我的包才将数据串在一起最后处理。但感觉这样太麻烦。同步Read感觉只有把整个Socket包拆分(数据很大的情况下)才满足需求。我服务端C++只是直接把整个Socket Send过来,想偷懒不写那么复杂觉得只有异步BeginRead才能满足我的需求。请问能提供异步BeginRead的示例参考吗?关于BeginRead我自己之前也有根据收集资料写了几次,但总不能得到我想要的结果。代码在13楼。您能帮我理清BeginRead的思路吗?万分感谢!
首先说c#提供的类没有这个bug。
要找到问题,建议你先不处理数据,Server侧只接收数据。
调试通讯程序,加断点的方法不靠谱,原因我不解释了
在client发送数据和Server接收数据的地方打印日志。
通过日志帮助找问题点。
要找到问题,建议你先不处理数据,Server侧只接收数据。
调试通讯程序,加断点的方法不靠谱,原因我不解释了
在client发送数据和Server接收数据的地方打印日志。
通过日志帮助找问题点。
原来调试不靠谱的啊多谢提醒,那我打日志找问题吧,谢谢
NetworkStream不靠谱吗那应该用什么方法接收数据?
http://www.cnblogs.com/dragon-L/p/3682967.html
里面有代码
while (m_Reader.DataAvailable || currentSize < packSize)
{
readSize = m_Reader.Read(bytes, 0, (int)m_ConnectSocket.ReceiveBufferSize);
if (readSize > 0)
{
currentSize += readSize;//读取成功,进行叠加
totalBytes = totalBytes.Concat(bytes).ToArray();//将读取的内容添加到容器后面
}
}其中这一段是有问题的,我将数据串起来采用的是 byte.Concat方法,而该方法是将bytes添加到totalBytes后面。bytes长度为1024。这代码在单机或网络环境好的情况是可以使用的,也是网上搜索给出的代码标准答案。但是如果在网络没那么好的情况下,是有问题的。Networkstream的Read方法是尽可能的往stream里面读取数据。如果网络不好的情况下,真实有用的数据可能只有512,但是Read中强行读取1024,导致bytes前512个数据是有用数据,而后面的512~1024数据则是之前的数据。而我的代码中直接把1024的bytes添加到结果后面,这样是不正确的。解决办法是类似下面的代码一样 if (readSize > 0)
{
LogRecord.WriteLog(readSize);
currentSize += readSize;
byte[] temp = new byte[readSize];
Array.Copy(bytes, 0, temp, 0, readSize);
totalBytes = totalBytes.Concat(temp).ToArray();//C#神器,byte[]会自动延长
}
多谢xian_wwq指出应该打印到日志内。项目中引用Log4Net将结果打到文件里面比对一下就发现问题了。至于zanfeng指出Networkstream并不可靠,但我并不知道改用什么其他方法接收。
以及chengbin0602指出的要异步接收。结贴给分,感谢各位!