Socket 知识总结
1.socket 概念
Socket套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
Socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力

通讯流程:

2.socket 断开重连
-
socket 的KeepAlive熟悉的作用
/** * When the keepalive option is set for a TCP socket and no data * has been exchanged across the socket in either direction for * 2 hours (NOTE: the actual value is implementation dependent), * TCP automatically sends a keepalive probe to the peer. This probe is a * TCP segment to which the peer must respond. * One of three responses is expected: * 1. The peer responds with the expected ACK. The application is not * notified (since everything is OK). TCP will send another probe * following another 2 hours of inactivity. * 2. The peer responds with an RST, which tells the local TCP that * the peer host has crashed and rebooted. The socket is closed. * 3. There is no response from the peer. The socket is closed. * * The purpose of this option is to detect if the peer host crashes. * * Valid only for TCP socket: SocketImpl
3. shutdownOutput /shutdownIutput vs close
- 在客户端或者服务端通过socket.shutdownOutput()都是单向关闭的,即关闭客户端的输出流并不会关闭。
- 服务端的输出流,所以是一种单方向的关闭流;
- 通过socket.shutdownOutput()关闭输出流,但socket仍然是连接状态,连接并未关闭
4.断开重连
- 客户端发送心跳包给服务器,服务器对收到的心跳包做应答
- 客户端发送心跳包给服务器,服务器对收到的心跳包不做应答。当监测到write方法报错,就重新建立连接(这种方法有问题,增加服务器压力)
5.阻塞和非阻塞
-
阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。函数只有在得到结果之后才会返回
-
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
6.cpu占用率
while (true) {
int i=0;
try {
if (socket.isConnected()&&in.available()>0) {//非阻塞方法,时时刻刻在执行
final String s = in.readUTF();//阻塞挂起cpu
this.onReceive(addr, s);
} //应该在else处 加入一个 睡眠,减缓cpu的执行
} catch (Exception e) {
runFlag = false;
}
}
7.socket 死锁
- c/s端 要遵循先读再写规则。先写在读会形成死锁
- c,s 端 都持有一个sendQueue和receiveQueue。当queue达到其最大容量,而没有即使read。就会造成死锁。
8. inputStream.read() 返回-1
通常在文件读取的时候,当read函数返回-1的时候就代表,文件读取结束,跳出循环。而Socket中read函数返回-1,并不是代表数据结束。read函数是阻塞函数,当没得数据的时候,将会一直阻塞在哪里。能读取到数据,则返回读取的字节数。返回-1,有如下情况:
调用了socket.close()
inputStream/outputStream.shutdowOut/InputStream
9.socket 粘包 和拆包
粘包概念 黏包实际上是对网络通信的一种优化,假如说上层只发送一个字节数据,而底层却发送了41个字节,其中20字节的I P首部、 20字节的T C P首部和1个字节的数据,而且发送完后还需要确认,这么做浪费了带宽,量大时还会造成网络拥堵。当然它还是有一定的缺点的,就是因为它会合并一些包会导致数据不能立即发送出去,会造成延迟,如果能接受(一般延迟为200ms),那么还是不建议关闭这种优化,如果因为黏包会造成业务上的错误,那么请改正你的服务端读取算法(协议),因为即便不发生黏包,在服务端缓存区也可能会合并起来一起提交给上层,推荐使用长度+类型+数据模式。 socket 有一个api 可以控制是否及时发送。 mSocket.setTcpNoDelay() TCP_NODEALY的默认值为false,表示采用Negale算法。如果调用setTcpNoDelay(true)方法,就会关闭Socket的缓冲,确保数据及时发送
拆包概念 数据链路层对数据包的上线是有要求的,一般要小于MTU。所以当数据包过大,将会拆包。那么服务端 读取的数据包可能出现 “半包”的情况。
解决方案: 由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,可以归纳如下。
(1)消息定长,例如每个报文的大小为固定长度200字节,如果不够,空位补空格;
(2)在包尾增加回车换行符进行分割,例如FTP协议;
(3)将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段,通常设计思路为消息头的第一个字段使用int32来表示消息的总长度;
(4)更复杂的应用层协议。
网友评论