磨刀不误砍柴工,让我们从概念入手,逐步深入。

所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。Socket通讯是我们开发多人在线游戏中的常用通讯方式,它主要有流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)两种类别,AS3中我们一般使用的是基于TCP的流式socket,因此本文也主要讲解这一种方式。既然这篇文章主要讲解的是流式socket,那让我们来看看什么是TCP.

TCP是一种流协议(stream protocol)。这就意味着数据是以字节流的形式传递给接收者的,没有固有的"报文"或"报文边界"的概念。

从这方面来说,读取TCP数据就像从串行端口读取数据一样--无法预先得知在一次指定的读调用中会返回多少字节( 也就是说能知道总共要读多少,但是不知道具体某一次读多少, 在AS3中的API表现为 socket. bytesAvailable. )。

为了说明这一点,让我们来看一个例子:我们假设在主机A和主机B的应用程序之间有一条TCP连接,主机A有两条报文D1,D2要发送到B主机,并两次调用send(as3中即flush)来发送,每条报文调用一次。

那么,我们自然而然的希望两条报文是作为两个独立的实体,在各自的分组中发送,如图1:

这样的话,我们无需做任何特别的处理,便能够很容易的区分每一个独立的数据,并根据需求分别做相应的处理。 但现实往往是有所偏差的,实际的数据传输过程很可能不会遵循这个模型。而是会采用以下四种方式之一进行传输。如图2:

  • D1和D2数据作为两个独立的分组,分别到达主机B;
  • D1和D2合为一个整体组,一起到达主机B;
  • D1的部分数据先到达主机B,剩下的D1数据和D2和在一组到达主机B;
  • D1和D2的部分数据先到达主机B, D2后到达主机B;

实际上,可能的情况还不止4种。 既 然是深入,那我们来看看为何会产生以上几种传输方式,可能有些开发人员感觉不需要了解这些,但还是极力推荐适当了解,我们要知其然,也要知其所以然,这其 实也为我们后面进行网络编程提供了理论依据和优化思路。言归正题,往下看,让我们来了解下TCP协议,我们知道TCP提供了全双工,可靠的传输服务。

TCP 通过以下方式来提供可靠性:

  • 应用数据被分割成T C P认为最适合发送的数据块。
  • 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
  • 当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒(一般200毫秒左右)。
  • TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错, TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。
  • 既然T C P报文段作为I P数据报来传输,而I P数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要, T C P将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
  • 既然I P数据报会发生重复, T C P的接收端必须丢弃重复的数据。
  • TCP 还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这将防止较快主机致使较慢主 机的缓冲区溢出,比如,发送方要发送8Kb的数据到接收端,但接收端的缓冲区已经只剩下2K的空间了,那么发送方最多只能发2Kb的数据,那么这时候不得 不把数据拆分发送。

而为了防止网络的拥塞现象,TCP提出了一系列的拥塞控制机制与算法,比如Nagle算法等,这些也可以看做是产生半包,黏包现象的其中一方面因素吧。

基于以上原因,TCP提供的是可靠的字节流服务,它不在字节流中插入记录标识符(当然这里说的是数据本身,而不是为了数据传输底层加入的一些信息,如图3), TCP 对字节流的内容也不作任何解释,它不知道传输的数据字节流是二进制数据,还是ASCII字符或者其他类型数据等,所以,对字节流的解释由TCP连接双方的应用层解释,这也是我们在写socket网络编程中所需 要做的事情。

如果想更多更深入了解网络方面的知识,可阅读这本经典书《TCP/IP详解》。 基于以上理论,我们可以来做个实验,使用AIR桌面程序做一个简单的服务端测试程序,以及建立一个AS工程客户端, 客户端和服务端连接成功之后,服务端发送200000次数据给客户端,每次发送一个整形数据后马上调用flush,代码如图4、图5:

图4

图5

当我们运行程序后发现,在200000次发送数据中,其实客户端最终的输出接收数据的次数远远少于200000次;那么这里有个结论:

当程序调用flush发送数据,只是从应用程序维护的一个缓存区拷贝到内核缓冲区,或者是说推入TCP协议栈中就返回了成功的标识,也就是说推进TCP协议栈的数据并不是马上发送的,TCP协议栈有自己的发送和控制策略。 所以这也解释了一些童鞋好奇为什么使用flush函数发送次数和数据接收事件响应次数不相等的原因以及半包、黏包现象产生的原因。

以 上讲的种种理论,最终还是为下面要讲的socket编程作铺垫的,标题写的深入,那么关于如何建立连接方面的这里不作讲解,可自行查阅相关内容,既然上面 我们说了,流式socket是以字节流的形式传输信息的,跟水流一样,没有边界,要做什么操作,传了什么内容,哪里是事情的开头,哪里又是事情的结尾,我 们完全是不知道的,所以我们一般在发送方需要对要传输的数据一定的格式,而接收方则按同样的规则解析这种格式,也就是我们常说的封包和拆包。也就是说我们 处理字节流是以包为单位进行处理的,包的结构一般如下,图6:

图6

一般是包头|Header|+包体|Body|+包尾|End|组成,有时候包尾部分也直接归入Body部分,形成|Header|+|Body|的结构。   
  
包尾部分的信息一般用于校验包的完整性或者合法性等。

包头长度,信息字段的位置一般都是固定的,必须包含的字段一般有“包的长度(packetLen)”和“操作码(cmd)”(如图6),提供长度是为了方面我们解析数据包,而提供操作码是为了让程序知道,该条信息是要做什么操作。除以上两个字段,其他字段根据需要增减。

Socket数据处理流程:

既然上面我们人为的给数据进行了封包,那么我们处理数据就是以包为单位进行处理的。

1、 因为包头|Header|长度固定,当收到数据时,先检验缓冲区有效数据长度,是否大于等于包头长度,如果小于包头长度,则说明数据不够,则继续等待下一次数据的到来, 如果大于包头长度,则读取包头长度的数据,并按一定的格式解析包头数据,从包头中获取到包的长度(packetLen),此时包头处理基本完毕。

2、 根据包头读取到的packetLen,从而计算出包体部分的数据长度(比如bodyLen),再判断缓存区剩余数据长度是否大于等于bodyLen,如果不是,同样等待, 如果是,则从缓存区读出包体部分,进而按照一定的格式读取里面的数据(例如readInt(),readShort()…);此时包数据基本读取完毕!

3、 上面我们说过网络中的字节流没有界线的,因此我们该知道每次到达的数据,也就是缓冲区的数据,有可能不止一个数据包,因此需要循环执行 1,2步。

验证包的完整性和合法性:

之前说过我们在包尾或者是包体的最后位置会适当加上一些信息,来验证包的完整性或者合法性。

举个例子,我们通过一定的规则,把包体的数据位置顺序打乱,最后在包尾加上规则信息,以便接收端能按同样的规则解析还原数据,再通过两端比较,就知道包是否完整和合法等    其实要验证包是否合法,有很多的方法。  
  
比如说判断包头操作码cmd的范围,传的数据长度是否跟包头声明的长度对应,或者整个包长度是否超过最大包的限制等等,不同的人有不同的做法,但目的基本是一样的。

时间有限,代码就不写了。

网络编程优化:

我们知道TCP有一系列的流量控制,网络拥塞控制的处理方案。但在AS3中,Adobe封装的太好了,导致很多底层东西AS目前都碰不了。 唯一我们能给点安慰的,就是基于TCP流量控制这一点上。

来重温一下:

TCP 还能提供流量控制。TCP 连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据,这将防止较快主机致使较慢主机的缓冲区溢出。

因此我们很自然的想到,尽量保持接收方TCP缓冲区有足够的容量,以便较快发送主机能及时发出数据,防止阻塞。

既然这样,那么AS客户端的一般保险处理方式是采取“快读取慢解析”的一种思路,也就是说当有接收到数据,我们会使用ByteArray一次性读取缓冲区的数据,然后再进行解包操作。

不过这里有个疑问,大伙可以研究探讨: Socket 类和ByteArray类同样实现了IDataInput, IdataOutput接口,那是不是socket其实已经实现了跟我们利用ByteArray存取数据的预防措施呢,也就是说内部数据存储部分跟 ByteArray具有同样的实现,如果是的话,那我们是不需要做 (&&)部分提及的把缓存区数据一次性写进 ByteArray后再进行包解析操作的方式,如果不是,那么还是实现 (&&):这一步来得保险,Adobe封装太好了,大伙可以研究下这里。

[转]关于AS3 Socket和TCP不得不说的三两事的更多相关文章

  1. Python的网络编程[0] -> socket[2] -> 利用 socket 建立 TCP/UDP 通信

    Socket 目录 socket 的 TCP/IP 通信基本建立过程 socket 的 UDP 通信基本建立过程 socket 的 UDP 广播式通信基本建立过程 socket 的多线程通信建立过程 ...

  2. C++ 利用socket实现TCP,UDP网络通讯

    学习孙鑫老师的vc++深入浅出,有一段时间了,第一次接触socket说实话有点儿看不懂,第一次基本上是看他说一句我写一句完成的,第二次在看SOCKET多少有点儿感觉了,接下来我把利用SOCKET完成T ...

  3. AS3: Socket 数据包 收 发

    AS3.0中使用Socket使用tcp服务器协议,它是一种流协议,不停的将分片传输给客户端,P作为流,发包是不会整包到达的,而是源源不断的. 它不同于UDP服务器协议,UDP作为数据包协议,整包到达. ...

  4. 网络层、传输层、应用层、端口通信协议编程接口 - http,socket,tcp/ip 网络传输与通讯知识总结

    引: http://coach.iteye.com/blog/2024511 什么是TCP和UDP,以及二者区别是什么? TCP的全称为传输控制协议.这种协议可以提供面向连接的.可靠的.点到点的通信. ...

  5. iOS之 HTTP、Socket、TCP的区别(易混

    一.HTTP 是一种超文本传输协议,全名hypertext transfer protocol,从字面意思上可以看出该协议用于规定客户端与服务端之间的传输规则, 传输的内容不限于文本(任意类型的数据) ...

  6. Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差

    Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别   Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...

  7. 【socket】TCP 和 UDP 在socket编程中的区别

    一.TCP与UDP的区别 基于连接与无连接  对系统资源的要求(TCP较多,UDP少)  UDP程序结构较简单  流模式与数据报模式  TCP保证数据正确性,UDP可能丢包  TCP保证数据顺序,UD ...

  8. SOCKET,TCP/UDP,HTTP,FTP

    (一)TCP/UDP,SOCKET,HTTP,FTP简析 TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层: 网络层:IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议 传 ...

  9. socket、tcp、http

    第一部分.概念的理解 1.什么是Socket? Socket又称之为“套接字”,是系统提供的用于网络通信的方法.它的实质并不是一种协议,没有规定计算机应当怎么样传递消息,只是给程序员提供了一个发送消息 ...

随机推荐

  1. Bash 中同名的内部命令和外部命令

    昨天有个人在 bug-bash 上问:为什么 [ --help 没有输出帮助信息.有人回答他了,原因是 coreutils 提供的 [ 命令才接受 --help 选项,Bash 自己的 [ 命令不接受 ...

  2. R的卸载和更新安装

      R包经常会遇到各种版本不兼容的毛病,比如当前的版本相较于包,新了/旧了都是麻烦 而升级R软件呢,最麻烦的就是之前安装的包怎么办? 搜罗了以下几种方法:   方法1: (1)直接安装新版本 (2)然 ...

  3. IOS系统基础知识

    在iOS应用中,每个程序得main函数中都调用了UIApplicationMain函数. 1 2 3 4 5 6 int main(int argc, char *argv[])  {      @a ...

  4. css3径向渐变详解-遁地龙卷风

    (-1)写在前面 我用的是chrome49,如果你用的不是.可以尝试换下浏览器前缀.IE在这方面的实现又特例独行了.不想提及-,这篇是为后续做准备. (0)快速使用 background-image: ...

  5. 前端开发必备!Emmet语法

    使用方法 emmet的使用方法也非常简单,以sublime text为例,直接在编辑器中输入HTML或CSS的代码的缩写,然后按tab键就可以拓展为完整的代码片段.(如果与已有的快捷键有冲突的话,可以 ...

  6. java3

    1:在定义Long或者Float类型变量的时候,要加L或者f. 整数默认是int类型,浮点数默认是double. byte,short在定义的时候,他们接收的其实是一个int类型的值. 这个是自己做了 ...

  7. 【安装Express】CentOS7 下安装NodeJs+Express+MongoDB+Redis

    上一篇介绍了一下怎么安装Nodejs,那么这一篇就说说怎么安装express,express有个中文站点非常非常方便,http://www.expressjs.com.cn/创建express框架的站 ...

  8. NSString相关操作

    //创建一个字符串对象 NSString * str_1 = @"Hello"; //字面量方法 ; NSString * str_2 = [NSString stringWith ...

  9. 【python】装饰器

    来源:廖雪峰 看了好多次装饰器,发现还是廖老师讲得好,能让我看懂..... 下面是一段装饰器代码 @log def now(): " 它的含义等价于 def now(): " no ...

  10. JTabbedPane 和 JScrollBar 联合使用

    需求:实现一个JTabbed, 当下拉到Tabbed的底部时,自动加载下一次的数据. 下面是具体代码: import java.awt.*; import javax.swing.table.Defa ...