大小端序及socket通信字节序问题

大端小端序概念

讲概念前,先插个小东西,之前搞混高字节、高地址、低字节、低地址这几个概念,之后理解大小端序就费劲了些。故画了下图:
图1

大端序(big-Endian):高字节保存在内存的低地址,低字节保存在内存的高地址。
小端序(little-Endian):高字节保存在内存的高地址,低字节保存在内存的低地址。

概念较抽象,可结合下图来理解:
图2

大小端优缺点

存在即是合理,大端的优点就是小端的缺点,反之亦然。
大端优点:符号位在所表示数据内存的第一个字节,便于快速判断数据的正负和大小。
小端优点:内存的低地址存放数据低字节,大数强制转换小数时效率高,直接丢弃高地址数据即可;cpu在做数值运算时依次从低到高取数运算即可,效率高效。

Socket通信字节序问题

IP/TCP网络传输时采用网络字节序(即大端序),这句话常常会被误读:认为网络传输时采用的是大端序。故在编写网络程序时,常常有困惑:在创建socket或bind时,要用htonl、htons等函数来将端口或ip地址从主机字节序转换成网络字节序,而在之后的send,recv等函数中为什么就没有使用这些函数了?

经过一翻搜索之后,思路也算理清了。IP/TCP协议栈内部是遵照标准来的,使用大端序来解析数据,因为端口及ip地址在拆解协议包时要使用到,因此使用者必须将它们转化成大端序。而send,recv等函数传输的只是字节流,不关心大小端序,这些字节流是交给用户层去处理的,至于用户层如何去使用,使用的对不对,那是用户层的事情。以下是不同cpu体系平台间传输数据示意图(用户层未处理字节序时的情况,主要为了说明send,recv不关心大小端序的问题,只是传输字节流):
图3
IP/TCP标准说传输时采用网络字节序,主要是为了解决不同平台之间的数据传输问题,如果要遵循这个标准的话,那么在send发送数据前就要调用htonl、htons等函数将本机字节序数据转化面网络字节序,在recv接收数据后,就要调用ntohl、ntohs等函数将网络字节序数据转化成本机字节序数据了。

因为现在大多数机器的CPU架构都是基于x86 (Intel、AMD等)体系的,故代码中就未考虑字节序的问题了(认为都是一样的字节序架构体系),故在send、recv等函数中用户层就没有再去htonl、htons、ntohl、htohs等函数了。从这可得知,若服务端运行在小端序机器,客户端运行在大端序机器,不考虑字节序问题的话,那结果就是不能工作了。

注:如果发送的是字符串,即在send前用sprintf等函数将数据全部转换成字符串,recv的也是字符串,然后在用户层自已去解析这些字符串,该转换成数字的就用atoi等函数去转换,那么就不需要去考虑网络字节序的问题了,单个字节没有字节序的问题啦。

问题拓展

为什么socket底层不帮忙处理字节序的问题的,send时,socket底层将主机字节序转换成网络字节序,然后recv时,socket底层又将网络字节序转换成主机字节序?
稍加思考,不难得出,socket不可能设计成干这事,如果要干这事,socket底层势必要知道数据流的结构才行。比如一个字节流由4个整数组成:2个字节整数,1个字节整数,1个字节整数,4个字节整数。用户层程序是知道字节流结构的,故可以挨个去调用转换,而socket 底层不知道具体的字节流构成,故无法去做转换了。

参考

https://blog.csdn.net/u014449821/article/details/80080672
https://blog.csdn.net/fysy0000/article/details/6622549
https://blog.csdn.net/qingtian506/article/details/53750398
https://www.v2ex.com/t/330173
http://blog.chinaunix.net/uid-15014334-id-4062785.html
http://www.52rd.com/Blog/Detail_RD.Blog_imjacob_17298.html

注:以上讲解完全是基于自己的理解所写,因为本人无大端序的机器,大端方面的未经实践的检验。若读者发现有错误,请务必批评指出,谢谢。

  • 13
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
SanNiuSignal是一个基于异步socket的完全免费DLL;它里面封装了Client,Server以及UDP;有了这个DLL;用户不用去关心心跳;粘包 ;组包;发送文件等繁琐的事情;大家只要简单的几步就能实现强大的通信系统;能帮助到大家是本人觉得最幸福的事情,也希望大家 在用的过程中找出DLL中不足的地方;好改正;此DLL的苹果版和java版正在努力开发中......交流QQ:365368248;此演示源码下载地址:http://pan.baidu.com/s/1eQw1npw 里面包括了SanNiuSignal.DLL 下面为大家介绍一下 如何使用SanNiuSignal. 1 ) :TCPServer服务器,通过静态方法TxStart.startServer注册成功服务器ITxServer;然后通过ITxServer设置好各种属性;最后启动 ITxServer.StartEngine();就可以了;具体可以到demo里去看;相对来说比较简单,下面来说一下需要注意的地方 一:ITxServer.BufferSize,就是接收数据缓冲区大小;默认为1kb,不要小于50字节,而且要和客户端设置成一样;否则会出现不可预知 的错误;这个缓冲区的大小,不会影响你每次发送数据的大小;大小只能提高你的发送速度 二:ITxServer.sendMessage 发送前先判断此IPEndPoint客户端是否在线;因为如果不在线等情况发生;此方法没有任何消息产生;如 果发送成功并且对方已经收到;会触发发送成功的事件; 三:用户可以通过ITxServer.FileLog记录服务器的运行信息; 2 ) :TCPClient客户端,通过静态方法TxStart.startClient(服务器地址或网址, 服务器端口号)注册成功客户端ITxClient;然后通过 ITxClient设置好各种属性;最后启动ITxClient.StartEngine();跟服务器启动差不多;具体可以到demo里去看;客户端要注意的一些地 方 一:如果非服务器强制关闭客户端的情况下,掉线的话,客户端默认是要重连的;他的原理是这样的,每10秒重连一次;重连 ITxClient.ReconnectMax次如果失败;会自动关闭客户端引擎;在重连的过程中;你可以改变客户端连接服务器的IP地址和端口号;但 如果当断开的时候不想重连;可以设置ITxClient.ReconnectMax=0; 二:登录篇,在引擎启动之前,用户可以设置ITxClient.OutTime来设置超时时间;默认为10秒,也就是说10秒之内肯定会有一个登录结果 ;登录成功或登录失败。有了登录结果会触发登录结果事件; 3 ) :Udp引擎,通过静态方法TxStart.startUdp()注册成功UDP--IUdpTx;然后通过IUdpTx设置好各种属性;最后启动 IUdpTx.StartEngine();如果在启动之前要绑定端口号,请在这里设置IUdpTx.Port;否则是随机使用本地端口;无论是UDP还是服务器和 客户端;它们的很多方法和属性以及事件都是相同的,因为他们都继承了通信系统的基接口ITxBase;下面是UDP引擎要注意的地方 一:UDP的优势在于速度快但不太可靠;所以有些属性不能设置的太过,如IUdpTx.BufferSize;默认为1KB,如果在广域网上发送信息, 缓冲区大小不要超过默认值;否则数据会丢失,如果一次性数据大于1KB;也没事,系统会自动分包发送;不太会丢包。 4 ) :文件发送系统,通过静态方法FileStart.StartFileSend(IFileSendMust)生成一个文件发送系统IFileSend;其中IFileSendMust是 必须实现的一个接口;具体参照demo;然后通过IFileSend设置好各种属性;发送还是要通过前面的三个引擎系统发送的;如 ITxClient.SendFile 这时会返回一个文件标签,是一个整数;IFileSend可以通过操作这个标签来操作这个正在发送的文件;接收文件 系统也一样;也是通过文件标签来操作;文件续传也是一样,也要通过前面的三个通信引擎系统进行续传;因为文件系统不能决定用户 通过哪个通信系统进行续传的;例如IUdpTx.ContinueFile进行续传;大家也可以试一下 掉线之后重新连接也可以续传; 5 ) :文件接收系统,通过静态方法FileStart.StartFileReceive(IFileReceiveMust)生成一个文件接收系统IFileReceive;其中 IFileReceiveMust是接收方必须实现的一个接口;具体参照demo;然后通过IFileReceive设置好各种属性;下面来讲讲文件系统要注意 的几个要点 一:每个文件都有一个文件标签,发送系统和接收系统是通过控制这个标签来控制这个文件的;如果是同一个文件传输,标签也一样的; 二:要注意属性BufferSize缓冲区大小,他是代表一次传送的字节数;如果这个文件缓冲区越大,传输速度越快;具体要设置成多少,大 家自己去调试;TCP传输的话一般可以设置成比较大,但UDP的话这个不要超过1KB;所以传输大文件尽量不要用UDP这种不可靠的东东; 三:当网络等各种原因中断文件的时候;会触发文件中断事件;文件自动处于暂停发送或接收状态;等待续传;续传的发送方,如果对方 同意续传;对方将不会触发续传开始的方法;别的大家可以通过demo里去发现; 总结: 其实这个DLL相对来讲是比较简单的;大家只要掌握这五个接口;1:ITxServer TCP服务器接口 2:ITxClient TCP客户端接口 3:IUdpTx UDP接口 4:IFileSend 文件发送方接口 5:IFileReceive 文件接收方接口 前面三个接口是通过TxStart启动;后面二个文件接口 是通过FileStart启动 今天先讲到这里;希望这个DLL对大家有所帮助;不足之处希望通过QQ或博客园大家一起交流;谢谢 此演示源码下载地址:http://pan.baidu.com/s/1eQw1npw 里面包括了SanNiuSignal.DLL
在 Android 中使用 Socket 进行网络通信,可以按照以下步骤进行: 1. 在 AndroidManifest.xml 文件中添加网络权限: ```xml <uses-permission android:name="android.permission.INTERNET"/> ``` 2. 在 Android 应用中创建一个 Socket 对象: ```java Socket socket = new Socket(ipAddress, portNumber); ``` 其中 ipAddress 是服务器的 IP 地址,portNumber 是服务器监听的端口号。 3. 获取输入输出流进行数据传输: ```java InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); ``` 通常情况下,发送数据时需要使用 OutputStream,接收数据时需要使用 InputStream。 4. 发送数据: ```java String message = "Hello, server!"; output.write(message.getBytes()); output.flush(); ``` 这段代码中,message 是要发送的数据,getBytes() 方法将字符串转换为字节数组,write() 方法将字节数组写入输出流中,flush() 方法将缓冲区中的数据刷新到输出流中。 5. 接收数据: ```java byte[] buffer = new byte[1024]; int length = input.read(buffer); String message = new String(buffer, 0, length); ``` 这段代码中,定义了一个大小为 1024 的字节数组 buffer,并通过 read() 方法从输入流中读取数据,length 表示读取到的字节数,最后将字节数组转换为字符串。 6. 关闭 Socket 连接: ```java socket.close(); ``` 在通信结束后,需要关闭 Socket 连接。 以上就是使用 Socket 进行网络通信的基本步骤,需要注意的是,在 Android 应用中不能在主线程中进行网络通信,需要使用异步线程或者线程池进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值