如题,TCP协议是面向对象的数据,先发的先到,后发的后到!但我个人理解是把每次的buffer数据拆开后的TCP包进行的比较,我用下面的代码连续发送三次数据,在网络环境极端不好的情况下,对于这种分别使用三次BeginSend发送的buffer数据,对方也使用BeginReceive这种异步接收模式,有可能导致第三次发送的buffer[2]数据较前面的数据buffer[0]和buffer[1]先到客户端吗?
int i=0;
while (i < 3)
{
Client.BeginSend(
buffer[i],
0,
buffer[i].Length,
0,
new AsyncCallback(SendCallback),
Client);//开启异步发送
i++;
}
int i=0;
while (i < 3)
{
Client.BeginSend(
buffer[i],
0,
buffer[i].Length,
0,
new AsyncCallback(SendCallback),
Client);//开启异步发送
i++;
}
你在哪一个异步Receive的demo上见过这种写法Client.BeginSend(buffer1,0,buffer1.Length,0,
new AsyncCallback(SendCallback),Client);
Client.BeginSend(buffer2,0,buffer2.Length,0,
new AsyncCallback(SendCallback),Client);
Client.BeginSend(buffer3,0,buffer3.Length,0,
new AsyncCallback(SendCallback),Client);
Client.BeginSend(buffer2,0,buffer2.Length,0,new AsyncCallback(SendCallback),Client);
Client.BeginSend(buffer3,0,buffer3.Length,0,new AsyncCallback(SendCallback),Client);
按照你的这种写法,你确实是顺序调用BeginSend,但是人家是异步并行发送数据,怎么可能按照这个顺序来发送数据?你在异步Receive上也是这种乱写异步代码的吗?
我这也是想进行即时通信,包括普通文字通信和文件通信。
如果是文字我就在数据前面加个字节0X11表示后面跟的是文字,即这种形式:0X11+文字数据
如果是文件就加0X21,即这种形式:0X21+文件数据。因为文件比较大,可能需要几次才能发完。那么我就担心这样一种情况的发生:
如果我在RECEIVE的时候得到的数据是:一个文件包的部分+文字包+此文件包的另一部分的情况啊,那么我程序中就可能错误把文字包也当做文件包的一部分而错误的处理了!
文件一般比较大,所以要分批发送,每次在BEGINSEND之前,自然是需要读取处理一下文件后才交给BEGINSEND发送。比如一个8K的文件,我每次读1K出来。然后再给加一个块号表示是第几次读出来,然后发送给远端处理,最后把8次得数据按顺序组合形成文件。实际上这样可能就是你说的“右手把消息按顺序一条一条发出去”
单独是文件发送的话,上面肯定没问题,可是即时通信中的聊天的数据怎么处理呢?文件数据肯定是持续发送的,如果此时突然有聊天的数据要发送,那这样一个右手是否就应付不过来了?
如果将发送信息和传送文件使用不同的SOCKET,客户端和服务器端都需要使用2个端口,服务器为保证安全,肯定需要与远程客户端有个验证过程的,那岂不是要验证多次?服务器开的端口越多,是否也就越不安全?
多此一举, TCP 协议回保证数据包顺序。
用两个 Socket, 独立的 Socket 是不会有顺序问题。。如果你把两个Socket 里面的数据搞混了,重写你的接受代码。
谁告诉你一定要开两个端口的IIS 默认用 web 服务用 80 端口,一次上 N 多多人浏览网页都行。
对,提醒的相当正确!可是服务器怎么多开2个SOCKET,分别处理文字和文件呢?能详细解释下吗?我觉得这个方法好!
10mb的文件
10个线程发送
第一个发送0-1mb
第二个发送1-2mb
.....如果使用一个socket不用担心到达顺序的问题,ip层已经帮你在每个包前面加了序号,到达之后就会自动排好,那片buffer【2】先到都没问题,虽然buffer【2】先到了,只有buffer【1】没到,你就读不到。你读到的都是已经拍好的。。
我觉得多开SOCKET好,CGabriel说的多开SOCKET好,可怎么多开呢?
晕你还是去补习基础知识先把客户端用 Socket.Connect ; 服务端若 Socket.Accept,就会返回一个 Socket...客户端 Connect 多几次,服务端就有几个 Socket...
………………………………………………………………
如果你先发送了一个的文件,又发送了一串文本,假设总的字节长度为1000,那么你在接收端读取的时候,用一个1000字节的byte数组来读,那么你读出来的就是一个文件+一串文本,顺序不是你能控制的。只要发送的时候用的是一个socket一次发送出去的。如果你发送了两次,并且你的接收端及时的接收了,那么他们就是不会粘再一起的。
lz还有一个误区:
我的意思是一个线程用一个socket,这一个socket发送的数据一定能自动在接收端排列好(只要发送成功),因为一个socket对应一个端口,所以这10个线程(也就是发送端用10个socket,客户端也用10个socket)的数据在接收端是不会混乱的。每个socket的顺序都是对的,那怕你在一个socket中先发了一个文件+字符串,到了接收端也一定是文件+字符串。socket只管发送接收,他只能能保证接收的和发送的是一致的。他的作用类似于一个能保证数据准确到达的运输管道,只管传输,不管传的是什么。。
所以lz说的,一个文件包的部分+文字包+此文件包的另一部分的情况?和socket没有关系。
要看lz怎么处理了。
lz说的这个问题是“粘包”
解决方法就是|命令(4bytes)//这4个字节的获取方法就是 BitConverter.GetBytes(长度),以下的类似
|----------------------
|附加消息长度(4个自己)
|----------------------
|数据长度(4个自己)
|----------------------
|附加消息
|----------------------
|数据
|----------------------这样就自己做了一个消息格式,前12个字节是命令,以后的字节是命令的附加消息+数据,最后我们把这条消息转成byte(),
比如有这样一条命令:
Login|User:admin,Pwd:admin|""
命令是登陆,附加消息中保存了用户名和密码,数据为空(这里只是举例)
因此可以这样做:List<byte> result = new List<byte>();
//前四位为命令
result.AddRange(BitConverter.GetBytes((int)Login));
//再四位为名字长度
if (附加消息!= null)
{
result.AddRange(BitConverter.GetBytes(附加消息.Length));
}
else
{
result.AddRange(BitConverter.GetBytes(0));
}
if (数据!= null)
{
result.AddRange(BitConverter.GetBytes(数据.Length));
}
else
{
result.AddRange(BitConverter.GetBytes(0));
}
if (附加消息!= null)
{
result.AddRange(附加消息);
}
if (byteData != null)
{
result.AddRange(数据);//上面提的的数据和附加消息要转为byte(),方法:Encoding.Unicode.GetBytes(数据);
}
return result.ToArray();//最后转成byte()
}
然后就是接收方了,他收到信息之后,看看是否大于12个字节,如果大于12个字节就取出头信息,取出命令,0-4个字节,方法BitConverter.ToInt32(msg,0),取出附加信息的长度:BitConverter.ToInt32(msg,4),取出数据长度:BitConverter.ToInt32(msg,8)
然后把附加消息的长度+数据的长度+12(消息头长度)=消息的长度,判断你收到的消息的总长度是否>=消息的长度,如果为True则可以拆包了,如果false则再度。。这样就不会粘包了,即使接收端读的时候一次读了两个包或两个半都没有问题。这是我个人的处理方法,肯定有更好的方法。可以参考我的代码:
http://topic.csdn.net/u/20110330/15/2bec10e1-7795-4153-b733-cfdcab17b3dd.html