从零开发B/S机架应用程序(五)

总结

在前面的两篇文章中介绍了UDP与TCP的使用,通过两种协议的比较,结合我们要开发的内容,功能的要求,我们采用TCP协议通信。


实现TCP/IP协议的通讯

原理

首先要理解基本的原理,2台电脑间实现TCP通讯,首先要建立起连接,在这里要提到服务器端与客户端,两个的区别通俗讲就是主动与被动的关系。
一个服务器可以接受多个客户端的连接,但是一个客户端只能连接一台服务器。
很简单的例子,你一台计算机开3个QQ,服务器怎么区分?所以准确的说是IP和端口号,但是客户端的端口号不是由你自己定的,是由计算机自动分配的,要不然就出现端口冲突了

Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。
socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息~~具体心跳消息格式是开发者自己定义的。

  • 短连接:
    连接->传输数据->关闭连接
    HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
    也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。
  • 长连接
    连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
    长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。

Socket传输适用范围

基于Socket传输的特点:Socket 传输方式适合于对传输速度,安全性,实时交互,费用等要求高的应用中,如网络游戏,手机应用,银行内部交互等

基于Http协议传输的适用范围

基于http协议传输的特点:基于http协议传输方式适合于对传输速度,安全性 要求不是很高,且需要快速开发的应用。如公司OA系统,互联网服务等。

Socket通信步骤:(简单分为4步)

1.建立服务端ServerSocket和客户端Socket

2.打开连接到Socket的输出输入流

3.按照协议进行读写操作

4.关闭相对应的资源

自定义通信协议

为什么要自定义网络协议,在前面的两讲中UDP与TCP的例子中,已经实现了简单的通信,但是这仅仅是初探门径,是无法应用到项目,或者实际的应用中的,
因为在TCP流传输的过程中,可能会出现分包与黏包的现象。我们为了解决这些问题,需要我们自定义通信协议进行封包与解包。

什么是半包、分包与黏包?

  • 半包 :指接受方没有接受到一个完整的包,只接受了部分,这种情况主要是由于TCP为提高传输效率,将一个包分配的足够大,导致接受方并不能一次接受完。(在长连接和短连接中都会出现)。
  • 分包:指接受方没有接受到一个完整的包,只接受了部分。
  • 黏包:指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

产生分包与黏包现象的原因是什么?

  • 产生分包原因:
    可能是IP分片传输导致的,也可能是传输过程中丢失部分包导致出现的半包,还有可能就是一个包可能被分成了两次传输,在取数据的时候,先取到了一部分(还可能与接收的缓冲区大小有关系),总之就是一个数据包被分成了多次接收。

  • 产生黏包的原因:
    由于TCP协议本身的机制(面向连接的可靠地协议-三次握手机制)客户端与服务器会维持一个连接(Channel),数据在连接不断开的情况下,可以持续不断地将多个数据包发往服务器,但是如果发送的网络数据包太小,那么他本身会启用Nagle算法(可配置是否启用)对较小的数据包进行合并(基于此,TCP的网络延迟要UDP的高些)然后再发送(超时或者包大小足够)。那么这样的话,服务器在接收到消息(数据流)的时候就无法区分哪些数据包是客户端自己分开发送的,这样产生了粘包;服务器在接收到数据后,放到缓冲区中,如果消息没有被及时从缓存区取走,下次在取数据的时候可能就会出现一次取出多个数据包的情况,造成粘包现象

什么是封包与解包?
TCP/IP 网络数据以流的方式传输,数据流是由包组成,如何判定接收方收到的包是否是一个完整的包就要在发送时对包进行处理,这就是封包技术,将包处理成包头,包体。
包头是包的开始标记,整个包的大小就是包的结束标。
之所以出现粘包和半包现象,是因为TCP当中,只有流的概念,没有包的概念.

什么时候需要考虑半包的情况?

我们了解到Socket内部默认的收发缓冲区大小大概是8K,但是我们在实际中往往需要考虑效率问题,重新配置了这个值,来达到系统的最佳状态。
1、使用的缓存大小为10k,这里使用的是短连接,所有不用考虑粘包的问题。
2、在并发量比较大的情况下,就会出现一次接受并不能完整的获取所有的数据。
- 处理方式:
1.通过包头+包长+包体的协议形式,当服务器端获取到指定的包长时才说明获取完整。
2.指定包的结束标识,这样当我们获取到指定的标识时,说明包获取完整。

什么时候需要考虑粘包的情况?

1.当时短连接的情况下,不用考虑粘包的情况
2.如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包
3.如果双方建立连接,需要在连接后一段时间内发送不同结构数据
- 处理方式:
接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开
解决方法(应用层维护消息和消息边界):
1、定长包
2、包尾加上\r\n标记(FTP)
3、包头增加包体长度。
4、复杂的应用层协议。
一句话:黏包最本质的原因就是接收方不知道接收的包有多大!
注:粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包

注意点

  • 包的长度:一个包没有固定长度,以太网限制在46-1500字节,1500就是以太网的MTU,超过这个量,TCP会为IP数据报设置偏移量进行分片传输,现在一般可允许应用层设置8k(NTFS系)的缓冲区,8k的数据由底层分片,而应用看来只是一次发送,windows的缓冲区经验值是4k。
  • Socket种类:Socket本身分为两种,流(TCP)和数据报(UDP),TCP作为流,发包是不会整包到达的,而是源源不断的到,那接收方就必须组包。而UDP作为消息或数据报,它一定是整包到达接收方。
  • 通信长度:这个是你自己决定的,没有系统强迫你要发多大的包,实际应该根据需求和网络状况来决定。对于TCP,这个长度可以大点,但要知道,Socket内部默认的收发缓冲区大小大概是8K,你可以用SetSockOpt来改变。但对于UDP,就不要太大,一般在1024至10K。
  • 通信接收
    1、关于接收,一般的发包都有包边界,首要的就是你这个包的长度要让接收方知道,于是就有个包头信息,对于TCP,接收方先收这个包头信息,然后再收包数据。一次收齐整个包也可以,可要对结果是否收齐进行验证,这也就完成了组包过程。UDP,那你只能整包接收了。
    2、要是你提供的接收Buffer过小,TCP将返回实际接收的长度,余下的还可以收,而UDP不同的是,余下的数据被丢弃并返回WSAEMSGSIZE错误。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多个发包,你必须分离它们,还有就是当Buffer太小,而一次收不完Socket内部的数据,那么Socket接收事件(OnReceive),可能不会再触发,使用事件方式进行接收时,密切注意这点。这些特性就是体现了流和数据包的区

协议格式

考虑点
  • 1、采用什么传输协议
    TCP还是UDP还是HTTP,即在哪一层进行传输
  • 2、采用什么传输格式
    即编码协议及序列化方式
    (1)采用什么字符集编码/解码
    (2)单个字符到字节的顺序(big-endian还是little-endian)
    (3)多个字符的分隔方式(采用分隔符还是定长方式,还是带字符长度的信息)
  • 3、消息的设计
    (1)消息

    定长,包括消息版本号(1字节)、消息序列号(4字节整型)、消息数据长度(可设计为包含消息类型块的长度,或者仅仅是消息体的长度)、消息延续标志(是/否,消息超长处理用)
    

    (2)消息类型块

    请求类型:响应类型、消息体长度
    回复类型:响应类型、消息体长度、错误信息(错误号、错误类型、错误内容)、消息处理标志
    

    (3)消息体

如何自定义协议?

目前业界主要采取的协议定义方式是:发送时数据包是由包头+数据 组成的:其中包头内容分为包类型+包长度。

下一篇代码实现是部分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值