问题描述:
浏览网页的时候,经常会看到文章中间出现一大段乱码,妨碍了阅读,这种情况是怎么产生的呢?如何解决阿?
非常感谢:)
解析:
乱码是怎么产生的呢?如果你打开一封乱码邮件时,你就会有可能看到如下两行:
Content-Transfer-Encoding: base64 (或qutoed Printable或8bit)
Content-Type: text/plain; charset=“us-ascii”
其中第一句说明此邮件传输时所用的编码,是base64还是qutoed Printable或是8bit,第二句Content-Type说明邮件的内容类型是纯文本还是或是超文本文档,charset说明信中文字所用的字符集。
有us-ascii,ISO-8859-1,GB2312等。为什么邮件传输时需要编码呢?因为最早的E-mail是UNIX系统中出现的,当时的E-mail只能传送ASCII码(美国国家标准信息交换码)格式的文字信息,ASCII码是7位代码,非ASCII码格式的文件在传送过程中就需要先编成7位的ASCII代码, 然后才能通过 E-mail进行传送;如果不经过编码,则在传送过程中会因为ASCII码7位的限制而被分解,分解之后只会让收信方看到一堆杂乱的ASCII字符。经过编码后的文件,在传送过程中可顺利传送,不会有“被截掉一位”的危险。但是收信方必须具有相应的解码程序,将这份经过编码的东西还原,才能看到发信人要传送的信息内容 。而我们的中文是8位代码编码的文字,并不是标准的ASCII码格式,由于在国内中文是通行的文字,所以大部分的国内邮件服务器都已能够处理GB内码的文件,因而不需要做这种编码/解码的 *** 作,但如果要送中文邮件到国外或使用国外的邮件服务器传送邮件,就需要经过这种转换才能传送,因为国外的邮件服务器是无法辨认中文内码的。中文码在经过一些不支持中文内码的传递主机时,依然会被截掉一位,造成文件乱码无法读取。而经过编码的中文邮件,收信人收到后将文件解码还原,也需要有中文系统才能看所写的中文信息。
电子邮件中常用的有三种编码标准
一、UU编码(Unix-to-Unix encoding)
uuencode和uudecode原来是unix系统中使用的编码和解码程序,基于Windows的类似程序有wincode和winzip等。 wincode除支持UU编码外也支持MIME、Binhex等编码格式,应用范围颇为广泛。
以上介绍的UU编码并非只能编中文文字。任何你要寄送的文件包括gif,exe等二进制文件都可以按照编码→发送→收信方收信→解码还原的步骤传送。这种编码方法现在虽已不常用,但OE,Foxmail等绝大多数邮件系统都支持。
二、MIME标准(Multipurpose Inter Mail Extentions)
UU编码解决了E-mail只能传送ASCII文件的问题 。但这种方式其实并不是很方便,因而又发展出一种新的编码标准,其全名是Multipurpose Inter Mail Extentions,一般译作“多媒体邮件传送模式”。
顾名思义,它可以传送多媒体文件,在一封电子邮件中附加各种格式的文件一起送出。
MIME标准现已成为Inter电子邮件的主流。它的好处是以物件作为包装方式,可将多种不同文件一起打包后传送。发信人只要将要传送的文件选好,它在传送时即时编码,收信人的软件收到也是即时解码还原,完全自动化,非常方便。当然先决条件是双方的软件都必须具有这种功能,要不然发信人很方便地把信送出去了,但收信人的软件如果没有这种功能,无法把它还原,看到的也就是一大堆乱码了。使用这种方式,用户根本不需要知道它是如何编码/解码的。即使只是用文字写的信,一样是打好包便寄出。如果是要寄多媒体文件,只要做选文件的动作,选完后寄出,其余的工作由电子邮件软件自动完成。 由于MIME的方便性,愈来愈多的电子邮件软件采用这种方式。(我们现在最常使用的电子邮件软件OutlookExpress、Eudora、Foxmail和一些网上在线邮局如163、263等就是采用MIME方式,所以我们才能如此轻松地收发电子邮件。
MIME定义的是一种规格,也可以说是一种统称。 其实能够符合这种规格的编码方式并不是单一的一种,只要符合这种MIME规格便可顺利传送。MIME定义两种编码方式:Base64与QP(Quote-Printable)。QP的规则是对于资料中的7位无须重复编码,仅将8位的数据转成7位。QP编码适用于非ASCII码的文字内容,例如我们的中文文件。而Base64的编码规则,是将整个文件重新编码成7位,通常用于传送二进制文件。编码的方式不同会影响编码之后的文件大小。而具有MIME功能的E-mail软件大都能自动判别你的邮件是采用何种编码,然后自动选择用 QP或Base64来解码。QP编码的方式,是将一个字节用两个16进制数值表示,然后在前面加“ =”。所以我们看到经过QP编码后 的文字通常是这个样子:
=A4j=AEa=A6n=A1I=A7=DA=AC0=B1=E7=A9s=A7g=A 1A=AB=DC=B0=AA=B而Base64编码是将连续的三个字符(二进制共24位)分解为四个六位二进制串, 每个串的大小就限定在0-63之间, 分别用ANSI字符'A'-'Z','a'-'z','0'-'9','+','/'代替; 如果最后的字符剩下一个, 补零后分解为两个六位二进制串, 编码的最后加进两个'='字符; 如果最后字符剩下两个,补零后分解为三个六位二进制串, 编码的最后加进一个'='字符。编码后的文字通常是: pGquYaZuoUmn2qxPseepc6dnoUGr3LCqv70ms773t3ymYqa5plakaq5hptu,如果你使用支持MIME标准的电子邮件软件,那么在收发信件的过程中是看不到这些乱七八糟的字符的。但如果很不幸,收信人没有这种支持MIME的软件,他看到的东西就是上面那一大段符号了。不过别急,你仍然可以先将这份“天书”存档,然后使用一些解码软件把它翻译回来。
三、Binhex编码
Binhex的编码方式常用于Mac机器,在PC上是较少使用的一种编码方式。一般PC上的电子邮件软件,亦多数支持MIME的规格,很少有支持Binhex格式。在常用的电子邮件软件中,唯Eudora具有这种功能,可直接解读Binhex的编码,如果你收到了这种由Binhex所编码的邮件,而且你的mail软件并不是Eudora或其他支持Binhex格式的软件,那也得用一个解读Binhex的程序解码。有一个共享软件Binhex3exe具有这个功能,它在许多FTP站点都能找到。
在Windows下,你还可以用上面介绍的wincode来解码。本文介绍的UU编码、MIME以及Binhex都可以用它来处理。但可惜的是,对于MIME,它只处理Base64的编码。如果能再加上QP的功能,真的可以靠它走遍天下了。
在MIME几乎已成标准规格的现在,用一套支持MIME的软件来做收发E-mail的工作,这些编码/解码工作就会自动完成,不会给你带来麻烦。如Foxmail30支持MIME和UUCode两种编码方式,但MIME只支持8位(8bit)编码方式。但他支持us-ascii,ISO-8859-1,GB2312三种字符集,且它只能以纯文本方式传送正文。而OE则支持UUCode和MIME中的Base64和QP两种编码方式,除此之外他还支持邮件正文以HTML方式传送。这就是我们用OE看到的有着漂亮背景的邮件。 当然如果你收到的八位编码的邮件,不幸中途被截了一位,造成若是不是上述Base64或QP样的乱码,且是纯中文的话,也可以通过编程序来补上最高位的方法恢复,如果是中英混合的,就只能让对方重发一遍了。
现在我们知道邮件的乱码是如何产生的了,如果遇到的话,相信你也可信“码”“邮”缰,游刃有余了。(信“码”“邮”缰在电子邮件中遇见乱码是一件让人感到很恼火的事,尤其是遇到有重要内容的邮件则更是损失惨重。人物名字上方的国泰民安是
服务器名称
,名字后面的994是代表服务器编号
一般在本服只显示自己的名字及称号,上天界或者云渺这种
全服
副本后就会显示某某服某某号,用来区分不同服务器的玩家,也是为了避免不同服务器玩家同名的的情况,每个编号代表一个服务器,同服务器的玩家编号相同
一、转码失败
在数据写入到表的过程中转码失败,数据库端也没有进行恰当的处理,导致存放在表里的数据乱码。
针对这种情况,前几篇文章介绍过客户端发送请求到服务端。
其中任意一个编码不一致,都会导致表里的数据存入不正确的编码而产生乱码。
比如下面简单一条语句:
set @a = "文本字符串";
insert into t1 values(@a);
变量 @a 的字符编码是由参数 CHARACTER_SET_CLIENT 决定的,假设此时编码为 A,也就是变量 @a 的编码。
2 写入语句在发送到 MySQL 服务端之前的编码由 CHARACTER_SET_CONNECTION 决定,假设此时编码为 B。
3 经过 MySQL 一系列词法,语法解析等处理后,写入到表 t1,表 t1 的编码为 C。
那这里编码 A、编码 B、编码 C 如果不兼容,写入的数据就直接乱码。
二、客户端乱码
表数据正常,但是客户端展示后出现乱码。
这一类场景,指的是从 MySQL 表里拿数据出来返回到客户端,MySQL 里的数据本身没有问题。客户端发送请求到 MySQL,表的编码为 D,从 MySQL 拿到记录结果传输到客户端,此时记录编码为 E(CHARACTER_SET_RESULTS)。
那以上编码 E 和 D 如果不兼容,检索出来的数据就看起来乱码了。但是由于数据本身没有被破坏,所以换个兼容的编码就可以获取正确的结果。
这一类又分为以下三个不同的小类:
1)字段编码和表一致,客户端是不同的编码
比如下面例子, 表数据的编码是 utf8mb4,而 SESSION 1 发起的连接编码为 gbk。那由于编码不兼容,检索出来的数据肯定为乱码。
2)表编码和客户端的编码一致,但是记录之间编码存在不一致的情形
比如表编码是 utf8mb4,应用端编码也是 utf8mb4,但是表里的数据可能一半编码是 utf8mb4,另外一半是 gbk。那么此时表的数据也是正常的,不过此时采用哪种编码都读不到所有完整的数据。这样数据产生的原因很多,比如其中一种可能性就是表编码多次变更而且每次变更不彻底导致(变更不彻底,我之前的篇章里有介绍)。举个例子,表 t3 的编码之前是 utf8mb4,现在是 gbk,而且两次编码期间都被写入了正常的数据。
3)每个字段的编码不一致,导致乱码和第二点一样的场景。不同的是:非记录间的编码不统一,而是每个字段编码不统一。举个例子,表 c1 字段 a1,a2。a1 编码 gbk,a2 编码是 utf8mb4。那每个字段单独读出来数据是完整的,但是所有字段一起读出来,数据总会有一部分乱码。
三、LATIN1
还有一种情形就是以 LATIN1 的编码存储数据
估计大家都知道字符集 LATIN1,LATIN1 对所有字符都是单字节流处理,遇到不能处理的字节流,保持原样,那么在以上两种存入和检索的过程中都能保证数据一致,所以 MySQL 长期以来默认的编码都是 LATIN1。这种情形,看起来也没啥不对的点,数据也没乱码,那为什么还有选用其他的编码呢?原因就是对字符存储的字节数不一样,比如 emoji 字符 "❤",如果用 utf8mb4 存储,占用 3 个字节,那 varchar(12) 就能存放 12 个字符,但是换成 LATIN1,只能存 4 个字符。
TCP是面向连接的协议。传输连接是用来传送TCP报文的,TCP连接传输的三个阶段分别为: 连接建立、数据传送和连接释放。
TCP连接的建立采用 客户服务器模式 。主动发起连接建立的应用进程叫做客户,而被动等待连接建立的应用进程叫做服务器。
TCP建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个TCP报文段,三次握手的过程如下图所示。
(2) 第二次握手 :服务器收到 SYN报文段后,如同意连接,则服务器会为该TCP连接 分配缓存和变量 ,并向客户端返回 确认报文段 ,在确认报文段中同步位 SYN = 1 和 确认位 ACK = 1,确认号 ack = x + 1,同时也为自己选择一个初始序号 seq = y。这时TCP服务器进程进入 同步收到(SYN-RCVD) 状态。
(3) 第三次握手 :客户进程在收到服务器进程的确认报文后,客户端为该TCP连接 分配缓存和变量 ,并向服务器端返回一个报文段,这个报文段是对服务器确认报文段进行确认,该报文段中 ACK = 1,确认号 seq = y + 1,而自己序号为 x + 1(即第二次握手服务器确认报文段的确认号)。客户端在发送ACK报文段后进入 已建立连接(ESTABLISHED) 状态,这时TCP连接已经建立。
当服务器收到客户端的确认后,也进入 ESTABLISHED 状态。
这样选择序号的目的是为了 防止由于网络路由TCP报文段可能存在延迟抵达与排序混乱的问题,从而而导致某个连接的一方对它作错误的解释 。
下图表示了建立连接使用固定的序号存在的问题:
由于一个TCP连接是被一对端点所表示的,其中包括2个IP地址和2个端口号构成的4元组,因此即便是同一个连接也会出现不同的实例,如果连接由于某个报文段长时间延迟而关闭,然后又以相同的4元组被重新打开,那么可以相信延迟的报文段又会被视为有效据重新进入新连接的数据流中,这就会导致数据乱序问题。
为了避免上述的问题, 避免连接实例间的序号重叠可以将风险降至最低 。
如前文所述,一个TCP报文段只有同时具备连接的4元组与当前活动窗口的序列号,才会在通信过程中被对方认为是正确的。然而,这也反应了TCP连接的脆弱性:如果选择合适的序列号、IP地址和端口号,那么任何人都能伪造一个TCP报文段,从而打断TCP的正常连接。所以使用初始化序号的方式(通常随机生成序号)使得序列号变得难猜,或者使用加密来避免利用这种缺点被攻击。
所以,可以明白在建立TCP连接时,客户端和服务器端初始化序列号,就避免了上述的问题。前面说过,TCP序号占32位,范围是0~2 32 - 1,并且可以重用。
假如 第一次握手可以携带数据的话,如果有人使用伪TCP报文段恶意攻击服务器,那么每次都在第一次握手中的SYN报文中携带大量的数据,因为它不会理会服务器的发送和接收能力是否正常,不断地给服务器重复发送这样携带大量数据的SYN报文,这会导致服务器需要花费大量的时间和内存来接收这些报文数据,这会将导致服务器连接资源和内存消耗殆尽。
所以,之所以第一次握手不能携带数据,其中的一个原因就是 避免让服务器受到攻击 。而对于第三次握手,此时客户端已经建立了连接,通过前两次已经知道了服务器的接收正常,并且也知道了服务器的接收能力是多少,所以可以携带数据。
根据前面描述,在第一次握手,客户端向服务发送建立连接请求,第二次握手,服务器同意建立连接,并向客户端返回一个确认报文,至此客户端已经知道了服务器同意建立连接,为什么客户端还需要对服务器的允许连接报文段进行确认?
第三个ACK报文段的目的简单来说主要是为了 实现可靠数据传输 。
三次握手的目的不仅在于让通信双方了解一个连接正在建立,还在于利用数据包的选项来承载特殊的信息,交换初始序列号(Initial Sequence,ISN) 。为了实现可靠传输,TCP协议通信双方,都必须维护一个序列号,以标识发送出去的数据报中,哪些是已经被对方收到的。三次握手的过程是通信双方想要告知序列号起始值,并确认已经收到序列号的必经过程。
如上图,在两次握手过程中,通信双方都随机选择了自己的初始段序号,并且第二次握手的时候客户端收到了自己的确认序号,确认了自己的序列号,而服务器端还没有确认自己的序列号,没有收到确认序号, 如果这时候两次握手下就进行数据传递, 序号没有同步,数据就会乱序。即如果只是两次握手,最多只有客户端的起始序列号能被确认,而服务器断的序列号则得不到确认。
在三次握手的过程中,服务器为了响应一个受到的SYN报文段,会分配并初始化连接变量和缓存,然后服务器发送一个SYNACK报文段进行响应,并等待客户端的ACK报文段。如果客户不发送ACK来完成该三次握手的第三步,最终(通常在一分多钟之后)服务器将终止该半开连接并回收资源。
这种TCP连接管理协议的特性就会有这样一个漏洞,攻击者发送大量的TCP SYN报文段,而不完成第三次握手的步骤。随着这种SYN报文段的不断到来,服务器不断为这些半开连接分配资源,从而导致服务器连接资源被消耗殆尽。这种攻击就是 SYN泛供攻击 。
为了应对这种攻击,现在有一种有效的防御系统,称为 SYN cookie 。SYN cookie的工作方式如下:
连接释放的四次挥手过程如下图所示:
(2) 第二次挥手 :服务器收到连接释放报文段后即发出确认,确认为ACK = 1,确认号为ack = u + 1,序号seq = v(其值是服务器前面已传送过的数据最后一个字节的序号加1),然后服务器就进入了 关闭等待(CLOSE-WAIT) 状态。
(3) 第三次挥手 :如果此时服务器没有数据要发送了,此时服务器向客户端发出 连接释放报文段 ,其FIN = 1,假设器序号为seq = w(在半关闭状态下服务器可能又发送了一些数据),服务器必须重复上次以发送的确认号ack = u + 1(因为客户端没有向服务器发送过数据,所以确认号和上次一致)。这时,服务器进入 最后确认(LAST-ACK) 状态,等待客户端的确认。
(4) 第四次挥手 :客户端在收到服务器端发出的连接释放报文段后,必须对此发出确认,在确认报文段中将ACK置位1,确认号ack = w + 1,而自己的序号为seq = u + 1。之后客户端进入 时间等待(TIME-WAIT) 状态。在经过 时间等待计时器 设置的时间 2MSL 后,客户端才进入 关闭(CLOSE) 状态
这是为了 保证客户端发送的最后一个ACK报文段能够到达服务器端。
客户端发送的ACK报文段可能丢失,因而使服务器收不到对自己已发送的释放连接报文段的确认。服务器会重传连接释放报文段,重新启动2MSL计时器,最终,客户端和服务器端都能进入CLOSE状态。
在建立连接时,服务器端处于LISTEN状态时,当收到SYN报文段的建立连接请求后,它可以把ACK报文段和SYN报文段(ACK报文段起确认作用,即确认客户端的连接建立请求;SYN报文段起同步作用)放在一起发送,所以在连接建立时四次握手(即第二次握手时,服务器的ACK报文段和SYN报文段分开发送)可以合并为三次握手。
而在释放连接时需要四次是因为 TCP连接的半关闭造成的 。由于TCP是 全双工 的(即数据可在两个方向上同时传递),因此,每个方向都必须要单独进行关闭,这个单方向的关闭就叫 半关闭 。在关闭连接时,当服务器收到客户端的FIN报文通知时,它仅仅表示客户端没有数据发送服务器了;但服务器未必将所有的数据都全部发送给了客户端,所以服务器端未必马上也要关闭连接,也即服务器端可能还需要发送一些数据给客户端之后,再发送FIN报文给客户端来表示现在可以关闭连接了,所以 它这里的ACK报文和FIN报文多数情况下都是分开发送的 ,这也是为什么释放连接时需要交换四次报文了。
TCP协议主为了在主机间实现高可靠性的包交换传输协议。本文将描述协议标准和实现的一些方法。因为计算机网络在现代社会中已经是不可缺少的了,TCP协议主要在网络不可靠的时候完成通信,对军方可能特别有用,但是对于政府和商用部门也适用。TCP是面向连接的端到端的可靠协议。它支持多种网络应用程序。TCP对下层服务没有多少要求,它假定下层只能提供不可靠的数据报服务,它可以在多种硬件构成的网络上运行。下面的图是TCP在层次式结构中的位置,它的下层是IP协议,TCP可以根据IP协议提供的服务传送大小不定的数据,IP协议负责对数据进行分段,重组,在多种网络中传送。TCP的上面就是应用程序,下面是IP协议,上层接口包括一系列类似于 *** 作系统中断的调用。对于上层应用程序来说,TCP应该能够异步传送数据。下层接口我们假定为IP协议接口。为了在并不可靠的网络上实现面向连接的可靠的传送数据,TCP必须解决可靠性,流量控制的问题,必须能够为上层应用程序提供多个接口,同时为多个应用程序提供数据,同时TCP必须解决连接问题,这样TCP才能称得上是面向连接的,最后,TCP也必须能够解决通信安全性的问题。
网络环境包括由网关(或其它设备)连接的网络,网络可以是局域网也可以是一些城域网或广域网,但无论它们是什么,它们必须是基于包交换的。主机上不同的协议有不同的端口号,一对进程通过这个端口号进行通信。这个通信不包括计算机内的I/O *** 作,只包括在网络上进行的 *** 作。网络上的计算机被看作包传送的源和目的结点。特别应该注意的是:计算机中的不同进程可能同时进行通信,这时它们会用端口号进行区别,不会把发向A进程的数据由B进程接收的。
进程为了传送数据会调用TCP,将数据和相应的参数传送给TCP,于是TCP会将数据传送到目的TCP那里,当然这是通过将TCP包打包在IP包内在网络上传送达到的。接收方TCP在接收到数据后会通信上层应用程序,TCP会保证接收数据顺序的正确性。虽然下层协议可能不会保证顺序是正确的。这里需要说明的是网关在接收到这个包后,会将包解开,看看是不是已经到目的地了,如果没有到,应该走什么路由达到目的地,在决定后,网关会根据下一个网络内的协议情况再次将TCP包打包传送,如果需要,还要把这个包再次分成几段再传送。这个落地检查的过程是一个耗时的过程。从上面,我们可以看出TCP传送的基本过程,当然具体过程可能要复杂得多。
在实现TCP的主机上,TCP可以被看成是一个模块,和文件系统区别不大,TCP也可以调用一些 *** 作系统的功能,TCP不直接和网络打交道,控制网络的任务由专门的设备驱动模块完成。TCP只是调用IP接口,IP向TCP提供所有TCP需要的服务。通过下图我们可以更清楚地看到TCP协议的结构。
上面已经说过了,TCP连接是可靠的,而且保证了传送数据包的顺序,保证顺序是用一个序号来保证的。响应包内也包括一个序列号,表示接收方准备好这个序号的包。在TCP传送一个数据包时,它同时把这个数据包放入重发队列中,同时启动记数器,如果收到了关于这个包的确认信息,将此包从队列中删除,如果计时超时则需要重新发送此包。请注意,从TCP返回的确认信息并不保证最终接收者接收到数据,这个责任由接收方负责。
每个用于传送TCP的通道都有一个端口标记,因为这个标记是由每个TCP终端确定的,因此TCP可能不唯一,为了保证这个数值的唯一,要使用网络地址和端口号的组合达到唯一标识的目的,我们称这个为了套接字(Socket),一个连接由连接两端的套接字标识,本地的套接字可能和不同的外部套接字通信,这种通信是全双工的。
通过向本地端口发送OPEN命令及外部套接字参数建立连接,TCP返回一个标记这个连接的名称,以后如果用户需要使用这个名称标记这个连接。为了保存这个连接的信息,我们假设有一个称为传输控制块(Transmission Control Block,TCB)的东西来保存。OPEN命令还指定这个连接的建立是主动请求还是被动等待请求。下面我们要涉及具体的功能了,TCP段以internet数据报的形式传送。IP包头传送不同的信息域,包括源地址和目的地址。TCP头跟在internet包头后面,提供了一些专用于TCP协议的信息。下图是TCP包头格式图:
源端口:16位;
目的端口:16位
序列码:32位,当SYN出现,序列码实际上是初始序列码(ISN),而第一个数据字节是ISN+1;
确认码:32位,如果设置了ACK控制位,这个值表示一个准备接收的包的序列码;
数据偏移量:4位,指示何处数据开始;
保留:6位,这些位必须是0;
控制位:6位;
窗口:16位;
校验位:16位;
优先指针:16位,指向后面是优先数据的字节;
选项:长度不定;但长度必须以字节记;选项的具体内容我们结合具体命令来看;
填充:不定长,填充的内容必须为0,它是为了保证包头的结合和数据的开始处偏移量能够被32整除;
我们前面已经说过有一个TCB的东西了,TCB里有存储了包括发送方,接收方的套接字,用户的发送和接收的缓冲区指针等变量。除了这些还有一些变量和发送接收序列号有关:
发送序列变量
SNDUNA - 发送未确认
SNDNXT - 发送下一个
SNDWND - 发送窗口
SNDUP - 发送优先指针
SNDWL1 - 用于最后窗口更新的段序列号
SNDWL2 - 用于最后窗口更新的段确认号
ISS - 初始发送序列号
接收序列号
RCVNXT - 接收下一个
RCVWND - 接收下一个
RCVUP - 接收优先指针
IRS - 初始接收序列号
下图会帮助您了解发送序列变量间的关系:
当前段变量
SEGSEQ - 段序列号
SEGACK - 段确认标记
SEGLEN - 段长
SEGWND - 段窗口
SEGUP - 段紧急指针
SEGPRC - 段优先级
连接进程是通过一系列状态表示的,这些状态有:LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT和 CLOSED。CLOSED表示没有连接,各个状态的意义如下:
LISTEN - 侦听来自远方TCP端口的连接请求;
SYN-SENT - 在发送连接请求后等待匹配的连接请求;
SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;
ESTABLISHED - 代表一个打开的连接,数据可以传送给用户;
FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
FIN-WAIT-2 - 从远程TCP等待连接中断请求;
CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
CLOSING - 等待远程TCP对连接中断的确认;
LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;
TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认;
CLOSED - 没有任何连接状态;
TCP连接过程是状态的转换,促使发生状态转换的是用户调用:OPEN,SEND,RECEIVE,CLOSE,ABORT和STATUS;传送过来的数据段,特别那些包括以下标记的数据段SYN,ACK,RST和FIN;还有超时,上面所说的都会时TCP状态发生变化。
下面的图表示了TCP状态的转换,但这图中没有包括错误的情况和错误处理,不要把这幅图看成是总说明了。
33 序列号
请注意,我们在TCP连接中发送的字节都有一个序列号。因为编了号,所以可以确认它们的收到。对序列号的确认是累积性的,也就是说,如果用户收到对X的确认信息,这表示在X以前的数据(不包括X)都收到了。在每个段中字节是这样安排的:第一个字节在包头后面,按这个顺序排列。我们需要认记实际的序列空间是有限的,虽然很大,但是还是有限的,它的范围是0到2的32次方减1。我想熟悉编程的一定知道为什么要在计算两个段是不是相继的时候要使用2的32次方为模了。TCP必须进行的序列号比较 *** 作种类包括以下几种:
(a) 决定一些发送了的但未确认的序列号;
(b) 决定所有的序列号都已经收到了;
(c) 决定下一个段中应该包括的序列号。
对于发送的数据TCP要接收确认,处理确认时必须进行下面的比较 *** 作:
SNDUNA = 最老的确认了的序列号;
SNDNXT = 下一个要发送的序列号;
SEGACK = 接收TCP的确认,接收TCP期待的下一个序列号;
SEGSEQ = 一个数据段的第一个序列号;
SEGLEN = 数据段中包括的字节数;
SEGSEQ+SEGLEN-1 = 数据段的最后一个序列号。
请注意下面的关系:
SNDUNA < SEGACK =< SNDNXT
如果一个数据段的序列号小于等于确认号的值,那么整个数据段就被确认了。而在接收数据时下面的比较 *** 作是必须的:
RCVNXT = 期待的序列号和接收窗口的最低沿;
RCVNXT+RCVWND-1 = 最后一个序列号和接收窗口的最高沿;
SEGSEQ = 接收到的第一个序列号;
SEGSEQ+SEGLEN-1 = 接收到的最后一个序列号;
上面几个量有如下关系:
RCVNXT =< SEGSEQ < RCVNXT+RCVWND 或 RCVNXT =< SEGSEQ+SEGLEN-1 < RCVNXT+RCVWND
测试的第一部分是检查数据段的开始部分是否在接收窗口中,第二部分是检查数据段的结束部分是否也在接收窗口内;上面两个检查通过任何一个就说明它包括窗口要求的数据。实际中的情况会更复杂一些,因为有零窗口和零数据段长,因此我们有下面四种情况:
段长度
接收窗口
测试
0
0
SEGSEQ = RCVNXT
0
>0
RCVNXT =< SEGSEQ < RCVNXT+RCVWND
>0
0
不可接受
>0
>0
RCVNXT =< SEGSEQ < RCVNXT+RCVWND或RCVNXT =< SEGSEQ+SEGLEN-1 < RCVNXT+RCVWND
请注意接收窗口的大小可以为零,在窗口为零时它只用来接收ACK信息,因此对于一个TCP来说,它可以使用零大小窗口在发送数据的同时接收数据。即使接收窗口的大小为零,TCP必须处理所有接收到信息的RST和URG域。
我们也应用计数的方式保护了一些特定的控制信息,这是通过隐式地使用一些控制标记使数据段能够可靠地重新发送(或确认)为达到的。控制信息并不在段数据空间中传送,因此,我们必须采用隐式指定序列号进行控制。SYN和FIN是需要保护的控制量,这两个控制量也只在连接打开和关闭时使用。SYN被认为是在第一个实际数据之间的数据,而FIN是最后一个实际数据之后的数据。段长度(SEGLEN)包括数据和序列号空间,如果出现了SYN,那么SEGSEQ是SYN的序列号。
初始序列号选择
协议对于特定连接被重复使用没有什么限制。连接是由一对套接字定义的。新的连接实例被定义为连接的另一次恢复,这就带来了问题:TCP如果确定多个数据段是从以前连接的另一次恢复中取得的呢?这个问题在连接迅速打开和关闭,或因为内存原因被关闭然后又迅速建立后显示特别突出。
为了避免混乱,用户必须避免因此恢复使用某一连接,而使序列号发生混乱。我们必须保证序列号的正确性,即使TCP失败,根本不知道以前的序列号是什么的情况下也要保证序列号的正确性。当新的连接被创建时,产生一个新的初始序列号(ISN)产生子,它用来选择一个新的32位ISN。产生子和32位时钟的低度位字节相关,低位字节的刷新频率大概是4微秒,因此ISN的循环时间大概是455小时。因此我们把网络包的最长生存时间(MSL)小于455小时,因此我们可以认为ISN是唯一的。对于每个连接都有发送序列号和接收序列号,初始发送序列号(ISS)由发送TCP选择,而初始接收序列号是在连接建立过程中产生的。
对于将要连接或初始化的连接,两个TCP必须和对方的初始序列号同步。这通过交换一个控制位SYN和初始序列号完成。我们把带有SYN的数据段称为"SYNs"。同步的获得过程这里就不重复了,每方必须发送自己的序列号并返回对对方序列号的确认。
1) A --> B SYN 本方序列号是X
2) A <-- B ACK 本方序列号被确认
3) A <-- B SYN 对方序列号是Y
4) A --> B ACK 确认对方序列号
上面的第2步和第3步可以合并,这时可以成为3阶段,所以我们可以称它为三消息握手。这个过程是必须的,因为序列号不和全局时钟关联,TCP也可以有不同的机制选择ISN。接收到第一个SYN的接收方不可能知道这个数据段是不是被延时,除非它记住了在连接上使用的最近的序列号(这通常是不可能的),因此它必须要求发送者确认。
为了保证TCP获得的确认是刚才发送的段产生的,而不是仍然在网络中的老数据段产生的,因此TCP必须在MSL时间之内保持沉默。在本文中,我们假设MSL=2小时,这是出于工程的需要,如果用户觉得可以,他可以改变MSL。请注意如果TCP重新初始化,而内存中的序列号正在使用,不需要等待,但必须确认使用的序列号比当前使用的要大。
如果一台主机在未保留任何序列号的情况下失败,那么它应该在MSL时间之内不发出任何数据段。下面将会这一情况进行说明。TCP的实现可以不遵守这个规定,但是这会造成老数据被当成新数据接收,而新数据被当成老数据拒绝的情况。
每当数据段形成并进入输出队列,TCP会为它指定序列空间中的一个值。TCP中多复本检测和序列算法都依赖于这个地址空间,在对方发送或接收之前不会超过2的32次方个包存在于输出队列中。所有多余的数据段都会被删除。如果没有这个规定,会出现多个数据段被指定同一个序列号的情况,会造成混乱。数据段中序列号的多少和数据段中的字节数一样多。
在通常情况下,TCP保留下一个要发送的序列号和还未确认的最老的序列号,不要在没有确认的时候就再次使用,这样会有些风险,也正是因为这样的目的,所以序列空间很大。对于2M的网络,要45小时来耗尽序列空间,因为一个数据段可能的最大生存时间也不过十几分之一秒,这就留下了足够的空间;而在100M的网络上需要54分钟,虽然少了点,但也可以了。
如果在实现TCP时没有为保存序列号留下空间,那清除多余的包可能就不能实现了,因此推荐这种类型的TCP实现最好在失败后等待MSL时间,这样保证多余的包被删除。这种情况有时候也可能会出现在保留序列号的TCP实现中。如果TCP在选择一个另一个TCP连接正在使用的序列号时,这台主机突然失败了,这就产生了问题。这个问题的实质在于主机不知道它失败了多久,也不知道多余的复本是不是还在网络中。
处理这种问题的方法是等待MSL时间,如果不这样就要冒着对方错误接收数据的危险,要等待的时间也就称为“沉默时间”。实现者可以让用户选择是不是等待,但是无论用户如何也不见得非要等待MSL时间。
34 建立一个连接
建立连接应用的是三消息握手。如果双方同时都发送SYN也没有关系,双方会发现这个SYN中没有确认,于是就知道了这种情况,通常来说,应该发送一个"reset"段来解决这种情况。三消息握手减少了连接失败的可能性。下面就是一个例子,在尖括号是的就是数据段中的内容和标记。其它的就不多说了。
在第2行,TCP A发送SYN初始化序列号,表示它要使用序列号100;第3行中,TCP B给出确认,并且期待着A的带有序列号101的数据段;第4行,TCP A给出确认,而在第5行,它也给出确认,并发送了一些数据,注意第4行的序列号与第5号的一样,因为ACK信息不占用序列号空间内的序列号。同时产生请求的情况如下图所示,只复杂一点。
使用三消息握手的主要原因是为了防止使用过期的数据段。为了这个目的,必须引入新的控制消息,RESET。如果接收TCP处理非同步状态,在接收到RESET后返回到LISTEN状态。如果TCP处理下面几种状态ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT时,放弃连接并通过用户。我们下面就详细说明后一种情况。
通过上面的例子,我们可以看出TCP连接是如何从过期数据段的干扰下恢复的。请注意第4行和第5行中的RST(RESET信号)。
半开连接和其它非正常状态
如果一方在未通过另一方的情况下关闭连接,或双方虽然失败而不同步的情况我们称为半开连接状态。在一方试图发送数据时连接会自动RESET。然而这种情况毕竟属于不正常情况。应该做出相应的处理。如果A处的连接已经关闭,B处并不知道。当B希望发送数据到A时,就会收到RESET信号,表示这个TCP连接有误,要中止当前连接。
假设A和B两个进程相互通信的时候A的TCP发生了失败,A依靠 *** 作系统支持TCP的存在,通常这种情况下会有恢复机制起作用,当TCP重新恢复的时候,A可能希望从恢复点开始工作。这样A可能会试图OPEN连接,然后在这个它认为还是打开的连接上传送数据,这时A会从本地(也就是A的)TCP上获得错误消息“未打开连接”。A的TCP将发送包括SYN的数据段。下面的例子将显示这一过程:
上面这个例子中,A方收到的信息并没有确认任何东西,这时候A发现出了问题,于是发送了RST控制信息。另一种情况是发生在A失败,而B方仍然试图发送数据时,下面的例子可以表示这种情况,请注意第2行中A对B发送来的信息不知所云。
在下面的例子中,A方和B方进行的被动连接,它们都在等待SYN信息。过期的包传送到B方使B回应了,而收到回应的A却发现不对头,传送RST控制信息,B方返回被动LISTEN状态。
现实中的情况太多了,我们列举一些产生RST控制信息的规则如下:通常情况下,RST在收到的信息不是期待的信息时产生。如果在不能确定时不要轻易发送RST控制信息。下面有三类情况:
如果连接已经不存在,而发送来的消息又不是RST,那么要返回RST。如果想拒绝对不存在的连接进行SYN,可以使用这种办法。如果到达的信息有一个ACK域,返回的RST信息可以从ACK域中取得序列号,如果没有这个域,就把RST的序列号设置为0,ACK域被设备为序列号和到达段长度之和。连接仍然处于CLOSE状态。
如果连接处于非同步状态(LISTEN,SYN-SENT,SYN-RECEIVED),而且收到的确认是对未发出包的确认或是接收到数据段的安全级别与不能连接要求的相一一致时,就发送RST。如果SYN未被确认时,而且收到的数据段的优先级比要求的优先级要高,那么要么提高本地优先级(得事先征得用户和系统的许可)要么发送RST;如果接收数据段的优先级比要求的优先级低,就算是匹配了,当然如果对方发现优先级不对提高了优先级,在下一个包中提高了优先级,这就不算是匹配了。如果连接已经进入SYN,那么接收到数据段的优先级必须和本地优先级一样,否则发送RST。如果到达的信息有一个ACK域,返回的RST信息可以从ACK域中取得序列号,如果没有这个域,就把RST的序列号设置为0,ACK域被设备为序列号和到达段长度之和。连接仍然处于与原来相同的状态。
如果连接处于同步状态(ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT),任何超出接收窗口的序列号的数据段都产生如下结果:发出一个空确认数据段,此段中包括当前发送序列号,另外还包括一个确认指出希望接收的下一个数据段的序列号,连接仍然保存在原来的状态。如果因为安全级,优先级之类的问题,那就发送RST信号然后进入CLOSED状态。
天互数据要想找到之前进入过的传奇盒子老区,可以按照以下步骤进行:
1 打开996传奇盒子客户端,选择登录账号。
2 在选择服务器的界面,找到“搜索”或“查询”类似的按钮。
3 点击“搜索”或“查询”按钮,输入您曾经进入过的老服务器名称或服务器编号,可以直接查找到之前进入过的服务器。
4 点击“进入游戏”即可进入之前选择的老区进行游戏。
需要注意的是,在找到之前进入的老区后,可能会发现角色已经不在原来的位置,甚至被删除了。这是因为服务器清理、合并等原因导致的。如果这种情况发生,可以联系客服寻求帮助,并对自己的账号和角色进行更好的保护。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)