socket通信注意事项

此博客是转载的,转载地址https://blog.csdn.net/waisock2017/article/details/78448427

(1) 对于可变包长,必须定义包头,并在包头中定义包的总长度

 由于socket是字节流,就像流水一样,对于传输多个包的socket数据流来说,从中间无法得知一个包的起始位置,从中间位置观察数据包的特征也是一个不靠谱的做法。因此,只能从包的第一个字节起,一个一个计算每一个包的位置。

 通常的做法是,对每一个传输的数据包,定义一个包头和包体。包头为固定长度,通常在其中定义版本号、包体的长度(或总包的长度)、以及包类型字段。包体通常是可变长度的内容。收到包的一方,解析socket数据流时,先解析固定长度的包头,从包头中得到整个包的长度,然后取得包体内容,紧接着取下一个包的包头和包体,如此循环下去,就能顺序收到每一个包。

(2) socket接收调用一次不保证收到一个完整数据包

调用socket的接收函数时,调用一次,可能会收到一个完整的对方发来的数据包,也可能只收到1/2个数据包,也可能1.5个数据包,也可能收到任何长度的包,或收到0个字节,这些情况都可能发生,这是正常情况,不是错误。当然,由于socket是全双工的字节流,你只可能收到一个一个字节,不可能收到半个字节,或2.5个字节。原因还是在于TCP是一个字节流,无头无尾,你只能根据socket收到的字节,自己来拼装成一个一个顺序的应用层的数据包。socket无法给你一个完整的数据包,只能给你一个一个收到的字节。

接收分为阻塞和非阻塞,默认是阻塞模式。在调用接收函数时,如果是阻塞模式,将造成程序阻塞在该函数中,长时间不响应,程序不能处理其他工作,造成用户体验差;如果是非阻塞模式,一次调用无论成功或是失败,函数立即返回,如果长时间无数据可接收,又会导致CPU为100%的问题。所以,这里一般都需要使用多线程处理,小心地处理各种正常和异常情况。

此外,网上一般都只有讨论阻塞的情况,对非阻塞的情况下的各种返回值,哪种是正常,哪种是阻塞,哪种是已经断开,资料非常少,需要自己的测试和验证去保证质量。

(3) 同理,socket发送调用一次也不保证整个包都发送出去

调用socket的发送函数时,调用一次,也不保证整个包发送完成。通常对于阻塞而已,只有发送完成或失败才返回;对于非阻塞类型,发送一部分就可能返回,因此,需要检查返回值,如果只发送了一部分,需要再次调用send函数,以便把剩下的数据包全部发送出去,才能发送下一个数据包。

与接收函数的调用类似,在调用发送函数时,如果是阻塞模式,将造成程序阻塞在该函数中,长时间不响应,程序不能处理其他工作,造成用户体验差;如果是非阻塞模式,一次调用无论成功或是失败,函数立即返回,如果长时间发送不出去,又会导致CPU为100%的问题。所以,这里一般也是需要使用多线程处理,小心地处理各种正常和异常的情况。

此外,网上一般都只有讨论阻塞的情况,对非阻塞的情况下的各种返回值,哪种是正常,哪种是阻塞,哪种是已经断开,资料非常少,需要自己的测试和验证去保证质量。

(4) socket收发可以同时进行

由于socket是全双工的字节流,全双工意味着接收的时候也可以同时发送,发送的时候也可以同时接收。因此,如果软件要求高性能,一般采用多线程处理,才能达到收和发同时进行的效果。

但是,需要注意,对于SSL而言,并不是全双工的,而是单工的,意味着SSL在发送的时候不能接收,接收的时候也不能发送。因此,SSL的效率,即使不考虑加密的影响,理论效率也只是普通socket的一半,如果考虑加密等因素,效率下降的就会更明显了。

(5) 大小字节序转换

  世界上的电脑CPU分为大端法和小端法两种。通常网络传输时都采用大端对齐法。对于AIX等系统,是大端对齐;而Windows、Linux等系统则是小端对齐。对于超过一个字节的short、int、int64及相应的unsigned数据类型,都需要进行大小字节序的转换。操作系统本身提供了ntohs和ntohl对16位和32位字节的整形进行转换,但并没有提供64位的字节序转换函数,此外,也没有提供float和double类型的数据类型进行转换的函数,怎么办呢?可以参考我写的另外一篇文章《socket编程必备函数:字节序转换C++模板函数,一劳永逸地代替ntoh或hton等函数》,这篇文章中,使用一个C++模板函数对所有的数据类型的转换进行了统一,使用简单方便,一劳永逸。

猜你喜欢

转载自blog.csdn.net/qq_40062917/article/details/85046864