网络协议详解

UDP

传输层协议,是一种无连接协议,数据传输不可靠,但传输速度快。

使用场景

UDP在某些对实时性要求较高而对可靠性要求相对较低的场景中具有优势。

  1. 实时视频和音频通信

    在视频会议软件(如Zoom、腾讯会议等)和在线直播应用中,UDP被广泛使用。因为视频和音频数据对实时性要求很高,即使偶尔丢失一些数据包,也不会对整体的观看体验产生太大影响。如果使用TCP,一旦出现数据包丢失,TCP会进行重传,这会导致视频卡顿。而UDP则会直接丢弃丢失的数据包,继续传输后续的数据,虽然可能会出现一些画面的轻微抖动或音频的短暂中断,但整体的流畅性更好。

  2. 在线游戏

    对于一些实时性要求较高的在线游戏,如射击游戏、赛车游戏等,UDP是首选的传输协议。在游戏中,玩家的操作指令和游戏状态的更新需要快速传输到服务器和其他玩家的客户端。使用UDP可以减少延迟,使得游戏的交互更加流畅。

代码实现

由于UDP是无连接的协议,所以使用时不需要像TCP那样建立连接。在Android中,直接通过DatagramSocketDatagramPacket进行数据的发送和接收即可。

  • 发送端代码示例

    // 创建DatagramSocket实例,系统分配端口
    DatagramSocket socket = new DatagramSocket();
    
    // 创建DatagramPacket实例,封装发送数据
    byte[] sendData = "Hello UDP".getBytes();// 创建要发送的数据
    InetAddress address = InetAddress.getByName("目标主机IP");
    int targetPort = 目标主机端口;
    DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, address, targetPort);
    
    // 发送数据
    socket.send(sendPacket);
    
    // 在通信完成后,关闭`DatagramSocket`以释放资源。
    socket.close();
    
  • 接收端代码示例

    // 创建DatagramSocket实例,需要指定本地端口用于接收数据
    int targetPort = 目标主机端口;
    DatagramSocket socket = new DatagramSocket(localPort);
    
    // 创建一个字节数组用于存储接收到的数据
    byte[] receiveData = new byte[1024];
    // 创建DatagramPacket实例,用于接收数据
    DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
    // 接收数据
    socket.receive(receivePacket);
    // 将接收到的数据转换为字符串
    String receivedString = new String(receivePacket.getData(), 0, receivePacket.getLength());
    
    // 在通信完成后,关闭`DatagramSocket`以释放资源
    socket.close();
    
TCP

传输层协议,是一种有连接协议,数据传输可靠,但传输速度相比UDP慢。

使用场景
  1. 网页浏览

    当用户在浏览器中输入网址并回车时,浏览器与Web服务器之间会通过TCP建立连接。例如,当你访问一个新闻网站时,浏览器通过TCP连接获取网页的HTML、CSS、JavaScript文件以及图片等资源。TCP的可靠性保证了网页内容能够完整、准确地展示在用户面前。

  2. 文件传输

    在文件传输协议(FTP)和简单文件传输协议(TFTP)中,TCP被广泛使用。例如,使用FTP客户端软件从服务器下载大文件时,TCP能够确保文件数据的完整性。即使在网络状况不佳的情况下,TCP的重传机制也能保证文件不会出现损坏,最终完整地保存在本地。

连接过程
  • 三次握手

    作用:确保双方准备就绪;确保数据包的顺序和完整性;结合SSL/TLS协议,通过证书验证和密钥交换。

    客户端服务器1. SYN (Seq=x) 客户端打招呼:我想和你建立连接客户端进入SYN_SENT状态2. SYN-ACK (Seq=y, Ack=x+1) 服务器回应:我也同意,这是我的回应服务器进入SYN_RCVD状态3. ACK (Ack=y+1) 客户端确认:好的,我们可以开始通信了客户端进入ESTABLISHED状态服务器也进入ESTABLISHED状态连接建立客户端服务器
断开过程
  • 四次挥手

    作用:确保双方在通信结束后能够安全、可靠地断开连接。

    客户端服务器1. FIN (Seq=x) 客户端说:我这边数据发完了,想断开连接客户端进入FIN_WAIT_1状态2. ACK (Ack=x+1) 服务器回应:我知道了,稍等一下服务器进入CLOSE_WAIT状态客户端进入FIN_WAIT_2状态3. FIN (Seq=y) 服务器说:我这边也处理完了,可以断开了服务器进入LAST_ACK状态4. ACK (Ack=y+1) 客户端确认:好的,断开连接客户端进入TIME_WAIT状态服务器进入CLOSED状态,连接断开客户端等待一段时间后也进入CLOSED状态客户端服务器
代码实现
  • 发送端代码示例

    // 创建Socket实例,连接到服务器
    Socket socket = new Socket("服务器IP地址", 服务器端口号);
    
    // 获取输出流,用于发送数据
    OutputStream outputStream = socket.getOutputStream();
    PrintWriter printWriter = new PrintWriter(outputStream, true);
    
    // 发送数据
    printWriter.println("Hello TCP Server");
    
    // 获取输入流,用于接收服务器响应
    InputStream inputStream = socket.getInputStream();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    
    // 接收服务器响应
    String response = bufferedReader.readLine();
    Log.d("TCPClient", "Server response: " + response);
    
    // 关闭资源
    printWriter.close();
    bufferedReader.close();
    socket.close();
    
  • 接收端代码示例

    // 创建ServerSocket实例,监听指定端口
    ServerSocket serverSocket = new ServerSocket(服务器端口号);
    
    // 接受客户端连接
    Socket clientSocket = serverSocket.accept();
    
    // 获取输入流,用于接收客户端数据
    InputStream inputStream = clientSocket.getInputStream();
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    
    // 接收客户端数据
    String request = bufferedReader.readLine();
    Log.d("TCPServer", "Client request: " + request);
    
    // 获取输出流,用于发送响应
    OutputStream outputStream = clientSocket.getOutputStream();
    PrintWriter printWriter = new PrintWriter(outputStream, true);
    
    // 发送响应
    printWriter.println("Hello TCP Client");
    
    // 关闭资源
    bufferedReader.close();
    printWriter.close();
    clientSocket.close();
    serverSocket.close();
    

    在实际的Android应用开发中,TCP通信通常会在单独的线程中进行,以避免阻塞主线程导致UI卡顿。同时,还需要处理各种异常情况,如网络连接异常、数据传输异常等,确保应用的健壮性和稳定性。

Https

SSL/TLS协议自动实现了加密传输过程,只要使用的证书是由受信任的证书颁发机构(CA)签发的,这一过程就会自动进行。理论上,中间人攻击中是难以被篡改请求的,因为伪造的证书在客户端证书校验过程中通常会被识别并拒绝。但是,如果客户端的证书验证机制存在漏洞或被绕过(例如信任所有证书),伪造的证书仍有可能通过校验,从而导致安全风险。因此,确保客户端严格验证证书的有效性和完整性至关重要,以有效防范中间人攻击。

客户端服务器发起HTTPS请求返回证书和公钥S验证证书合法性生成对称密钥K,使用公钥S加密后发送解密得到对称密钥K使用对称密钥K加密数据发送使用对称密钥K解密数据客户端服务器
自签名证书

如果我们不是CA证书,而是服务器的自签名证书,就要自己实现证书验证的过程。下面是 okhttp 的证书验证代码:

  • public static OkHttpClient createOkHttpClient(Context context) throws Throwable {
        // 生成信任管理器,验证服务器证书。
        TrustManager[] tm = getTrustManagers();
        // 生成密钥管理器,验证客户端证书。
        KeyManager[] km = getKeyManagers();
    
        // 创建SSLContext,用于管理SSL/TLS连接
        SSLContext sslContext = SSLContext.getInstance("TLS");
        // 单向认证(客户端认证服务器证书)时,km传null。双向认证需要传km
        sslContext.init(km, tm, new SecureRandom());
        // 创建SSLSocketFactory,用于创建支持SSL/TLS的套接字
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
    
        // 创建一个信任所有主机名的HostnameVerifier,根据项目中的域名判断
        HostnameVerifier hostnameVerifier = (hostname, session) -> true;
    
        // 创建OkHttpClient
        return new OkHttpClient.Builder()
            .sslSocketFactory(sslSocketFactory, (X509TrustManager) tm[0])
            .hostnameVerifier(hostnameVerifier) // 信任所有主机名
            .build();
    }
    
    // 生成信任管理器,验证服务器证书。
    private static TrustManager[] getTrustManagers() throws Throwable {
        // 加载自签名证书
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        InputStream caInput = context.getAssets().open("your_certificate.crt");
        Certificate ca = cf.generateCertificate(caInput);
        caInput.close();
    
        // 创建KeyStore并添加证书
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);
    
        // 创建TrustManagerFactory,用于生成信任管理器
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(keyStore);
        return tmf.getTrustManagers();
    }
    
    // 生成密钥管理器,验证客户端证书。
    private static KeyManager[] getKeyManagers() throws Throwable {
        // 加载客户端证书
        KeyStore clientKeyStore = KeyStore.getInstance("BKS");
        InputStream keyInput = context.getAssets().open("client.bks");
        clientKeyStore.load(keyInput, "password".toCharArray());
        keyInput.close();
    
        // 创建KeyManagerFactory,用于生成密钥管理器
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(clientKeyStore, "password".toCharArray());
        return keyManagerFactory.getKeyManagers();
    }
    
防代理

可以通过以下两种方式来防止代理:

  1. 使用Proxy.NO_PROXY

    OkHttpClient client = new OkHttpClient.Builder()
        .proxy(Proxy.NO_PROXY)
        .build();
    
  2. 修改系统代理设置

    System.setProperty("http.proxyHost", "");
    System.setProperty("http.proxyPort", "");
    System.setProperty("https.proxyHost", "");
    System.setProperty("https.proxyPort", "");
    
检测代理
// 检测是否被代理
public static boolean isWifiProxy(Context context) {
    final boolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
    String proxyAddress;
    int proxyPort;
    if (IS_ICS_OR_LATER) {
        proxyAddress = System.getProperty("http.proxyHost");    //获取代理主机
        String portStr = System.getProperty("http.proxyPort");  //获取代理端口
        proxyPort = Integer.parseInt((portStr != null ? portStr : "-1"));
    } else {
        proxyAddress = android.net.Proxy.getHost(context);
        proxyPort = android.net.Proxy.getPort(context);
    }
    Log.i("代理信息","proxyAddress :"+proxyAddress + "prot : " + proxyPort);
    return (!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1);[^36^][^37^][^40^]
}
安全问题
  • HTTPS欺骗

    攻击者将用户从HTTPS页面重定向到HTTP页面,从而获取未加密的数据。

    客户端攻击者服务器发起HTTPS请求 (https://example.com)攻击者拦截请求重定向到HTTP页面 (http://example.com)用户被重定向到HTTP页面发起HTTP请求 (http://example.com)请求被发送到攻击者控制的服务器返回HTTP响应 (未加密数据)攻击者获取未加密数据提交敏感信息 (用户名、密码等)攻击者截获敏感信息可选:转发请求到真实服务器 (https://example.com)返回HTTPS响应 (加密数据)可选:转发响应到用户 (篡改或未篡改)攻击者维持通信,用户不知情客户端攻击者服务器
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值