UDP
传输层协议,是一种无连接协议,数据传输不可靠,但传输速度快。
使用场景
UDP在某些对实时性要求较高而对可靠性要求相对较低的场景中具有优势。
-
实时视频和音频通信
在视频会议软件(如Zoom、腾讯会议等)和在线直播应用中,UDP被广泛使用。因为视频和音频数据对实时性要求很高,即使偶尔丢失一些数据包,也不会对整体的观看体验产生太大影响。如果使用TCP,一旦出现数据包丢失,TCP会进行重传,这会导致视频卡顿。而UDP则会直接丢弃丢失的数据包,继续传输后续的数据,虽然可能会出现一些画面的轻微抖动或音频的短暂中断,但整体的流畅性更好。
-
在线游戏
对于一些实时性要求较高的在线游戏,如射击游戏、赛车游戏等,UDP是首选的传输协议。在游戏中,玩家的操作指令和游戏状态的更新需要快速传输到服务器和其他玩家的客户端。使用UDP可以减少延迟,使得游戏的交互更加流畅。
代码实现
由于UDP是无连接的协议,所以使用时不需要像TCP那样建立连接。在Android中,直接通过DatagramSocket和DatagramPacket进行数据的发送和接收即可。
-
发送端代码示例
// 创建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慢。
使用场景
-
网页浏览
当用户在浏览器中输入网址并回车时,浏览器与Web服务器之间会通过TCP建立连接。例如,当你访问一个新闻网站时,浏览器通过TCP连接获取网页的HTML、CSS、JavaScript文件以及图片等资源。TCP的可靠性保证了网页内容能够完整、准确地展示在用户面前。
-
文件传输
在文件传输协议(FTP)和简单文件传输协议(TFTP)中,TCP被广泛使用。例如,使用FTP客户端软件从服务器下载大文件时,TCP能够确保文件数据的完整性。即使在网络状况不佳的情况下,TCP的重传机制也能保证文件不会出现损坏,最终完整地保存在本地。
连接过程
-
三次握手
作用:确保双方准备就绪;确保数据包的顺序和完整性;结合SSL/TLS协议,通过证书验证和密钥交换。
断开过程
-
四次挥手
作用:确保双方在通信结束后能够安全、可靠地断开连接。
代码实现
-
发送端代码示例
// 创建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)签发的,这一过程就会自动进行。理论上,中间人攻击中是难以被篡改请求的,因为伪造的证书在客户端证书校验过程中通常会被识别并拒绝。但是,如果客户端的证书验证机制存在漏洞或被绕过(例如信任所有证书),伪造的证书仍有可能通过校验,从而导致安全风险。因此,确保客户端严格验证证书的有效性和完整性至关重要,以有效防范中间人攻击。
自签名证书
如果我们不是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(); }
防代理
可以通过以下两种方式来防止代理:
-
使用
Proxy.NO_PROXYOkHttpClient client = new OkHttpClient.Builder() .proxy(Proxy.NO_PROXY) .build(); -
修改系统代理设置
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页面,从而获取未加密的数据。
3775

被折叠的 条评论
为什么被折叠?



