1.网络通信的前提
两个不同主机的进程要进行网络通信首先就是需要找到对方,找到唯一能标识进程的方法。网络层的IP地址可以唯一标识一个主机,传输层协议和端口号可以唯一标识主机的一个进程。因为我们可以用(ip地址+协议+端口号)唯一标识一个网络中的进程。
2.什么是socket
我之前一直不懂什么是socket,真的是一直一直都不懂。我昨天终于觉得我懂了!我所理解的socket是客户端和服务器通信的一个通道,客户端有一个clientSocket,服务器有一个severSocket,每一个socket要指明主机地址和端口号,这样就可以确定是和哪个主机上的哪个进程通信了。socket是应用层和传输层之间通信的一个接口。
3.TCP和UDP
TCP:传输控制协议,有连接,速度相对较慢,可靠数据传输
UDP:用户数据报协议,无连接,速度快,不可靠数据传输
关于tcp三次握手(我觉得上学期的老师讲的很好):
tcp三次握手❓seq:发送的数据段在整个数据中第一个字节的位置
ack:期待对方发送的下一个字节(累积的)
为什么是三次握手而不是两次握手呢?
1.两次握手可能出现的问题就是,因为数据在网络传输的过程中有延迟,客户端第一次向服务器握手的时候可能过了一段时间没有收到服务器的回应,这个时候客户端就重新发送请求,等到服务器收到这个请求又开启了一个连接,然而这个连接是无效的。因为是两次握手,那么只要server发出确认,新的连接就建立了。然而这个时候客户端已经关闭了连接(没有开启连接的请求),服务器开启这个连接也是没有用的,客户端不会理睬也不会传递数据,这个时候就浪费了许多服务器上的资源。
而三次握手,是在客户端再次向服务器发送确认消息的时候,连接才建立的,就不会出现上面的问题。
三次握手只能是客户端主动发起的,当客户端的socket发起connect请求的时候,就会发起三次握手。
四次挥手是连接取消的过程,服务器和客户端都可以主动发起,执行close方法的时候就会触发四次挥手。
四次挥手的过程:
tcp四次挥手tcp四次挥手的过程:
因为tcp连接是
双向的,任何一方都可以发起关闭连接的请求,当一方的数据发送完毕以后想对方发一个fin信号,代表这一方向上不再发送数据。
比如图中,client向server发送了finbit,代表client到server的数据传输终止。
server向client发送一个ack,但是此时并没有终止连接,仍然可以继续从server向client发送数据,直到server数据传输完毕,才向client发送一个finbit信号。
client接收到服务器发送的finbit,给服务器发送确认信号,连接终止。
为什么是四次挥手而不是三次挥手呢?
服务器第一次收到来自客户端的fin信号的时候,可能还没有传输完所有的数据,只是先给客户端发送一个确认报文,告诉客户端自己收到了信号。然后继续把所有的数据发完,发完以后再发送fin信号关闭这一方向上的数据传输。
4.socket连接与http连接的区别
socket是持续连接的,只要客户端和服务器连接上,就可以相互发送数据,直到连接断开。所以socket适合用来实现即时通信。如果需要服务器主动向客户端发数据,就需要用到socket连接。
http采取的是请求响应模式,客户端向服务器发送请求服务器才会回传数据给客户端。服务器不会主动向客户端发送数据。
5.socket长连接与心跳包
socket本身是tcp连接的,就是长连接,那为什么还需要心跳包呢。
虽然在tcp连接中,客户端和服务器是一直连接着的,但是如果客户端很长时间都不给服务器发消息,有些节点(防火墙)会清除这些不活跃的连接。这样的话,等到再想发数据的时候连接已经断开了。
这个时候就需要心跳包,告诉服务器客户端还活着,实现了保活。同时还要处理断线问题,检测出断线以后根据需求进行响应。
在iOS中实现心跳包,其实非常简单,在client连接到服务器的回调方法里声明一个定时器,每个一段时间调用一个方法。
#pragma mark - 连接成功回调
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString*)host port:(UInt16)port{
NSLog(@"socket连接成功");// 每隔30s像服务器发送心跳包
self.connectTimer =[NSTimerscheduledTimerWithTimeInterval:30target:self selector:@selector(longConnectToSocket) userInfo:nilrepeats:YES];
// 在longConnectToSocket方法中进行长连接需要向服务器发送的讯息
[self.connectTimer fire];}
longConnectToSocket方法的实现:
-(void)longConnectToSocket{
// 根据服务器要求发送固定格式的数据,假设为指令@"longConnect",但是一般不会是这么简单的指令
NSString*longConnect =@"longConnect";
NSData*dataStream = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[self.socket writeData:dataStream withTimeout:1tag:1];}
如果断线了的话需要判断是用户主动断线还是服务器断线,如果是用户主动断线不需要恢复连接,如果是服务器断线的话需要自动恢复连接。
6.为什么会发生数据粘连?
客户端在向服务器发送数据的时候调用了writeData方法,这个时候并不是一下子就把所有的数据都发送出去了,而是把要发送的数据存在客户端的数据缓冲区里,由socket协议决定什么时候把这些数据发出去。
而服务器在接收数据的时候也不是一下子就把所有的数据接收了,而是把接收到的数据存到缓冲区里,然后从缓冲区里接收数据。如果有一部分数据还没有到达缓冲区,正在传输,这个时候就会造成数据的不完整。
7.解决方法
在发送/接收消息的时候讲消息的长度作为消息的一部分发送出去。发送/接收消息的时候先把消息放到自己的缓冲区,根据消息的长度判断缓冲区里的数据够不够一个完整的消息,如果够的话就从缓冲区里取出整个消息,如果不够就继续读。这样就避免了把多个消息混合在一起,也避免了读到不完整的消息。
网友评论