使用Socket遇到的问题--Problems with Tcp Messages

使用Tcp时注意:Tcp不提供Message boundaries,即不区分信息间的边界。
如果Client的2个Send(),一个发送200bytes,一个发送100个bytes,那么Server收到了300个Bytes放到了TcpBuffer,怎样区分这300个Bytes呢?怎样保证需要的2个Receive()可以收到所需要的信息呢?
Receive() 方法是从Tcp Buffer中读取数据,不是从Network直接读取数据。当从Network收到新的Tcp Packet时,Tcp Package顺序的放入Tcp Buffer中,当调用Receive()时,Receive()会读取Tcp Buffer中有的,可以装满Receive()的接收数组的信息.

2个要注意的问题:
 1.不要在同一台计算机上测试Client程序和Server程序,因为同实际环境会有差别
 2.假设Tcp会像我们在程序中定义的那样发送数据
//
如果要解决Tcp发送信息没有边界的问题,需要使用一些技巧,使远程计算机可以分辨信息边界:
  1.一直使用固定大小的信息
  2.每次先发送信息的实际大小
  3.使用定界符
以上3种方法都可以使远程系统能够分辨信息边界,每种方法都有自己的优缺点。
对于方法1,这种方法最简单,却有最耗费网络资源;另一个优点是,如果收到多条信息,那么可以清楚地根据收到信息的字节数来分辨每条信息。
使用这种方法发送固定长度信息时,必须保证所有信息使用Send()发送完毕,不能假设Tcp会把信息完整的发送。依赖于Tcp Buffer的大小和要发送信息的大小,Send()有可能不会将所有信息发送出去的。Send()的返回值是实际发送到Tcp Buffer的字节数,根据这个返回的字节数,可以判断是否完整的发送了信息。可以使用一个简单的循环,来确保所有信息正确发送。 代码如下:
//

int SendData(Socket s, byte[] data) 

  
int total = 0
  
int size = data.Length; 
  
int dataleft = size; 
  
int sent; 
  
while (total < size) 
  

    sent 
= s.Send(data, total, dataleft, SocketFlags.None); 
    total 
+= sent; 
    dataleft 
-= sent; 
  }
 
  
return total; 
}
 


对于Receive(),同样,要确保收到预定义长度的字节,同样使用循环处理,代码如下:
//

byte[] ReceiveData(Socket s, int size) 

int total = 0
int dataleft = size; 
byte[] data = new byte[size]; 
int recv; 
while(total < size) 

recv 
= s.Receive(data, total, dataleft, 0); 
if (recv == 0

data 
= Encoding.ASCII.GetBytes("exit "); 
break
}
 
total 
+= recv; 
dataleft 
-= recv; 
byte[] ReceiveData(Socket s, int size) 

  
int total = 0
  
int dataleft = size; 
  
byte[] data = new byte[size]; 
  
int recv; 
  
while(total < size) 
  

    recv 
= s.Receive(data, total, dataleft, 0); 
    
if (recv == 0
    

      data 
= Encoding.ASCII.GetBytes("exit "); 
      
break
    }
 
   total 
+= recv; 
   dataleft 
-= recv; 
   }
 
  
return data; 
}
  

使用固定长度数组发送,接收信息,对于比较小的信息,需要做填充处理,比较费资源。
好的解决办法是,发送不定长度的字节,但是处理Tcp边界是个问题,比较好的解决办法是,先用小的数组发送信息长度,然后发送信息,这就是方法2。
如果在信息头上加上4个字节的Integer数据标示发送信息长度,那么可以发送最大65K的数据。
注意Integer数据,转换为数组时使用byte[] bs=BitConvert.GetBytes(someinteger),收到后还原使用:int Len=BitConverter.ToInt32(bs,0);如果Server和Client是相同类型的机器,如intel based的windows系统,那么不会有问题,如果不是,那么可能会有问题,是Int类型数据在转换为字节数组用网络传输,转换的方式不同的缘故,即高位优先和低位优先的问题。


对于第3种方法,使用定界符:就是在每条信息的末尾加入特殊的字符,标记信息结束。当接收方收到数据后,需要每个字符的检查,找到定界符后,标示整条信息,后续的数据,又从新开始检查,这对于收到大的信息,检查比较耗费资源,而且定界符的定义必须明确,要保证传送内容中没有定界字符才可以。对于发送文本信息,C# 定义了一些类可以简单处理这种操作,如StreamReader的ReadLine()方法。

参考:
Sybex C# Network Programming

posted on 2005-03-30 09:58  Pierce  阅读(1519)  评论(0编辑  收藏  举报

导航