tcp
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的
udp
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
粘包
1、发送端需要等缓冲区满才发送出去,有可能会发生两个数据接口的数据合并到一个缓冲区进行传输,造成粘包
2、接收方可能由于网络原因不及时接收缓冲区的包,造成多个包接收,形成粘包
拆包
当发送端缓冲区的长度大于网络的数据链路层的最大传送单元时,tcp会将数据拆成几个数据包发送出去,形成拆包
字节序概念
比如,一个占据四个字节的 16 进制数0x12345678,决定其大小的最重要的字节是“12”,最不重要的是“78”。小端字节序将最不重要的字节排在前面,储存顺序就是78563412;大端字节序则完全相反,将最重要的字节排在前面,储存顺序就是12345678。
as代码实例
this.socket = new Socket();
this.socket.endian = Endian.BIG_ENDIAN;//大端字节序
this.socket.timeout = _timeout;
this.socket.connect(serverIp, serverPort);
this.socket.addEventListener(Event.CONNECT, onSocketConnected);
this.socket.addEventListener(Event.CLOSE, onSocketClosed);
this.socket.addEventListener(IOErrorEvent.IO_ERROR, onSocketError);
this.socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecErrorHandler);
this.socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketRecvData);
private function onSocketRecvData(event:ProgressEvent):void{
while (true){
if (dataLen == 0){//数据包长度判断
if (!socket.connected){
return;
}if (socket.bytesAvailable < 5){//这边5是看发送端和接收端之间定义的协议,当前这边前四个字节指定包的长度,第五个字节表示包是否压缩
break;
}
dataLen = socket.readInt()-1;//包长度
recvCompressFlag = socket.readByte();//是否压缩
}else{
if (socket.bytesAvailable < dataLen){//如果可读数据长度小于包长,没必要读取数据,等待下次数据包到来再进行读取(这边就是拆包的处理方式)
break;
}
var bytes:ByteArray = new ByteArray();
bytes.objectEncoding = ObjectEncoding.AMF3;//编码格式
bytes.endian = Endian.BIG_ENDIAN;//数据的字节读取顺序
socket.readBytes(bytes, 0, dataLen);//读取字节流数据,只读取对应包的数据长度,这边可能会发生粘包情况,剩余的包数据循环再次读取
dataLen = 0;//重置数据包长度
if(recvCompressFlag){
bytes.uncompress();
}
onPacket(bytes);//解析数据包
}
}
}