Android进程间通信之Socket使用与解析(TCP、UDP)

在使用Socket实现进程间通信前,先对网络协议相关知识进行简单回顾。

一、网络分层

一般情况会将网络分为5层:

  • 应用层 常见协议:HTTP、FTP、POP3等
  • 传输层 常见协议:TCP、UDP
  • 网络层 常见协议:IP
  • 数据链路层
  • 物理层

在这里插入图片描述
在这里插入图片描述

二、TCP与UDP区别
  • TCP:面向连接的、可靠的流协议,提供可靠的通信传输。
    • 所谓流,就是指不间断的数据结构,你可以把它想象成排水管道中的水流。当应用程序采用TCP发送消息时,虽然可以保证发送的顺序,但还是犹如没有任何间隔的数据流发送给接收端。
    • 有顺序控制、丢包重发机制
  • UDP:面向无连接的,具有不可靠性的数据报协议。(让广播和细节控制交给应用的通信传输)
    • 无顺序控制、丢包重发机制

TCP用于在传输层有必要实现可靠传输的情况,由于它是面向连接并具有“顺序控制”、重发控制等机制;而UDP则主要用于那些对高速传输和实时性有较高要求的通信或广播通信。
因此TCP和UDP应该根据应用的目的按需使用,没有绝对优缺点。

三、TCP三次握手与四次挥手

使用TCP协议的连接建立与断开,正常过程下至少需要发送7个包才能完成,就是我们常说的三次握手,四次挥手。
在这里插入图片描述

1、标志位Flags、序号
  • 序列号 Sequeuece number(seq): 数据包本身的序列号,初始序列号是随机的。
  • 确认号 Acknowledgment number(ack): 在接收端,用来通知发送端数据成功接收
  • 标志位,标志位只有为 1 的时候才有效
    • SYN(synchronize):表示在连接建立时用来同步序号。
    • ACK:TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1.
    • FIN(finish):用来释放一个连接。当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。
2、三次握手

三次握手:是指建立一个TCP连接时需要客户端和服务端总共发送3个包确认连接的建立。在Socket编程中,这一个过程由客户端执行connect来触发。
在这里插入图片描述

  1. 第一次握手:客户端向服务端发送请求报文;即SYN=1,ACK=0,seq=x。
  2. 第二次握手:服务端收到客户端的请求报文,服务端会确认应答,告诉客户端已经收到请求了;即SYN=1,ACK=1,seq=y,ack=x+1;
  3. 第三次握手:客户端收到服务端的确认应答后,再次向服务端进行确认应答,建立完整的连接;即ACK=1,seq=x+1,ack=y+1

为什么要进行三次握手呢,或两次确认??
在这里插入图片描述
下面使用Wireshark抓包工具体验下三次握手的过程
在这里插入图片描述
红色框内就是一个TCP建立连接的过程

  1. 53324 —>80:嘿,哥们,我想访问你的web资源,能不能把你的80端口打开
  2. 80 —> 53324:可以啊,我已经把80端口打开了,为了保证我们的数据能可靠传输,你那边也需要把53324端口打开;
  3. 53324 —> 80:没问题,我已经把53324端口打开了,尽管的发送数据过来吧。

下面看看在三次握手的标志位的变化

1、53324 —>80
在这里插入图片描述
2、80 —> 53324
在这里插入图片描述
3、53324 —> 80
在这里插入图片描述

3、四次挥手

四次挥手:即终止TCP连接,就是断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在Socket编程中,这一工程由客户端或服务端任意一方执行close来触发。

由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这一原则是当一方数据发送完成,发送一个标志位为 FIN
的报文来终止这一方向的连接,收到标志位为 FIN 的报文意味着这一方向上不会再收到数据了。但是在 TCP
连接上仍然能够发送数据,直到这一方向也发送了 FIN 。发送 FIN 的一方执行主动关闭,另一方则执行被动关闭。

在这里插入图片描述

  1. 第一次挥手:客户端发送一个FIN=1,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。意思是说”我客户端没有数据要发给你了”,但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。
  2. 第二次挥手:服务器端收到FIN后,先发送ack=u+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入FIN_WAIT_2
    状态,继续等待服务器端的FIN报文。
  3. 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=1报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入LAST_ACK状态。
  4. 第四次挥手:客户端收到FIN=1报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送ack=w+1后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。服务器端收到ACK后,就知道可以断开连接了。客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。
四、Socket介绍
1、Socket定义
  • 即套接字,是应用层 与 TCP/IP 协议族通信的中间软件抽象层,表现为一个封装了 TCP / IP协议族 的编程接口(API)
  1. Socket不是一种协议,而是一个编程调用接口(API),属于传输层(主要解决数据如何在网络中传输)
  2. 即:通过Socket,我们才能在Andorid平台上通过 TCP/IP协议进行开发
  3. 对用户来说,只需调用Socket去组织数据,以符合指定的协议,即可通信

在这里插入图片描述

  • 位于传输层,是网络上的两个程序通过一个双向的通信连接 实现数据的交换的一种进程通信方式之一。
  • 成对出现,一对套接字
 Socket socket = new Socket("localhost", 8888);
 localhost:IP地址
 8888:端口号
 即: IP地址 -- 端口号成对出现

socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供了网络开发所用的接口;HTTP是轿车,提供了封装或显示数据的具体形式;socket是发动机,提供了网络通信的能力。

典型的应用就是C/S结构:
在这里插入图片描述
从图可知,socket的使用是基于TCP或UDP协议。

2、Socket建立连接过程

在这里插入图片描述

3、原理

Socket的使用类型主要有两种:

  • 流套接字(streamsocket) :基于 TCP协议,采用 流的方式 提供可靠的字节流服务
  • 数据报套接字(datagramsocket):基于 UDP协议,采用 数据报文 提供数据打包发送的服务

具体原理图如下:

在这里插入图片描述

4、Socket 与 Http 对比
  • Socket属于传输层,因为 TCP / IP协议属于传输层,解决的是数据如何在网络中传输的问题
  • HTTP协议 属于 应用层,解决的是如何包装数据

由于二者不属于同一层面,所以本来是没有可比性的。但随着发展,默认的Http里封装了下面几层的使用,所以才会出现Socket & HTTP协议的对比:(主要是工作方式的不同):

  • Http:采用 请求—响应 方式。
  1. 即建立网络连接后,当 客户端 向 服务器 发送请求后,服务器端才能向客户端返回数据。
  2. 可理解为:是客户端有需要才进行通信
  • Socket:采用 服务器主动发送数据 的方式
  1. 即建立网络连接后,服务器可主动发送消息给客户端,而不需要由客户端向服务器发送请求
  2. 可理解为:是服务器端有需要才进行通信
5、Socket java简单实现

我们知道socket是基于TCP或UDP协议实现的,下面以TCP协议为例实现,因为TCP更加常用些。

1.1 使用步骤
  • 客户端
  1. 创建socket对象,指定成对的服务端IP地址和端口号
  2. 通过socket获取输出流,写入数据发给服务端
  3. 通过socket获取输入流,接受服务端的发送的数据
  4. 关闭资源close
  • 服务端(与服务端类似)
  1. 创建ServerSocket对象,并指定端口号,其端口号必须与客服端一致
  2. 通过ServerSocket对象,获取客户端的socket实例(ServerSocket.accept方法)
  3. 通过socket获取输入流,接受客户端发来的消息
  4. 通过socket获取输出流,写入数据向客户端发送数据作为回应
  5. 关闭资源close
1.2 具体实例

客户端Client

public class Client {
   

    private static final String TAG = "Client";

    public static void main(String[] args) {
   

        new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                //IO操作不能放在主线程执行
                connectServer();
            }
        }).start();

    }

    private static void connectServer() {
   
        try {
   
            //1、创建客户端socket,指定服务端地址和端口
            Socket socket = new Socket("localhost", 8888);
            boolean connected = socket.isConnected()
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值