• 10.网络编程套接字Socket


    目录

    一、网络编程

    1.发送端和接收端

    2.请求和响应

    3.客户端和服务端

    二、网络套接字Socket 

    1.流套接字

    1.1java.net.DatagramSocket类

    1.2Server类

     1.3Client类

    1.4Log类

    2.数据报套接字

    2.1java.net.ServerSocket类

    2.2Server类

    2.3Client类

    2.4Log类

    3.端口被占用问题

    3.1寻找端口方法1

    3.2寻找端口方法2

    3.3解决方法

    4.TCP的长连接和短连接

    4.1短连接-Client

    4.2长连接---Client

    4.3长连接---Server

    4.4长连接---Client(用户输入)


    一、网络编程

    指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)

    即便是同一个主机,只要是不同进程,基于网络来传输数据, 也属于网络编程。

    1.发送端和接收端

    在一次网络数据传输时:

    发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机

    接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机

    收发端:发送端和接收端两端,也简称为收发端

    发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念 

    2.请求和响应

    一般来说,获取一个网络资源,涉及到两次网络数据传输:

    第一次:请求数据的发送

    第二次:响应数据的发送

    3.客户端和服务端

    服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务

    客户端:获取服务的一方进程,称为客户端。

    客户端获取服务资源 ;客户端保存资源在服务端

    二、网络套接字Socket 

    Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基 于Socket套接字的网络程序开发就是网络编程

    分为:流套接字、数据报套接字和原始套接字

    1.流套接字

    使用传输层TCP协议(传输控制协议),传输层协议。

    TCP: 有连接 可靠传输 面向字节流 有接收缓冲区,也有发送缓冲区 大小不限

    1.1java.net.DatagramSocket类

    DatagramSocket 是UDP Socket,用于发送和接收UDP数据报

     

     java.net.DatagramSocket类

    实现 同一主机下的进程 -> 进程 ---- 请求响应模型

    1.2Server类

    1. import java.io.*;
    2. import java.net.*;
    3. import java.util.Arrays;
    4. import java.util.HashMap;
    5. public class Server {
    6. // 1. 写在文件中
    7. // 2. 写在数据库中
    8. private static final HashMap<String, String> map = new HashMap<>();
    9. static {
    10. map.put("apple", "苹果");
    11. map.put("banana", "香蕉");
    12. }
    13. public static void main(String[] args) throws IOException {
    14. Log.println("服务器 bind 在 8080 端口上");
    15. DatagramSocket socket = new DatagramSocket(8080);
    16. while (true) {
    17. // 读取请求
    18. byte[] buf = new byte[1024];
    19. DatagramPacket received = new DatagramPacket(buf, buf.length);
    20. Log.println("服务器准备接收数据");
    21. socket.receive(received); // 服务器会阻塞
    22. Log.println("服务器接收到了数据");
    23. // 处理
    24. // 对方的地址(ip)
    25. InetAddress address = received.getAddress();
    26. Log.printf("对方的地址是: %s\r\n", address);
    27. // 对方的端口(port)
    28. int port = received.getPort();
    29. Log.printf("对方的端口是: %d\r\n", port);
    30. // 真正收到的数据长度
    31. int n = received.getLength();
    32. Log.printf("一共收到的数据长度: %d\r\n", n);
    33. // 真正有用的数据
    34. byte[] actualData = Arrays.copyOf(buf, n);
    35. // byte[] + 字符集编码 -> String
    36. String request = new String(actualData, 0, actualData.length, "UTF-8");
    37. Log.printf("请求是: %s\r\n", request);
    38. // 分析请求
    39. if (!request.startsWith("我想进行翻译工作\r\n")) {
    40. // 不符合应用层的请求协议,不做处理
    41. Log.println("请求头不符合格式");
    42. continue;
    43. }
    44. if (!request.endsWith("\r\n")) {
    45. // 不符合应用层的请求协议,不做处理
    46. Log.println("请求尾不符合格式");
    47. continue;
    48. }
    49. String englishWord = request.substring("我想进行翻译工作\r\n".length(), request.length() - 2);
    50. Log.println("收到的英文单词是: " + englishWord);
    51. String chineseWord = map.getOrDefault(englishWord, "不认识");
    52. Log.println("翻译后的中文是: " + chineseWord);
    53. // 发送响应
    54. String response = String.format("好的\r\n%s\r\n", chineseWord);
    55. Log.println("响应是: " + response);
    56. // String + 字符集编码 -> byte[]
    57. byte[] bytes = response.getBytes("UTF-8");
    58. Log.println("准备发送响应");
    59. DatagramPacket sent = new DatagramPacket(
    60. bytes, 0, bytes.length,
    61. address,
    62. port
    63. );
    64. socket.send(sent);
    65. Log.println("响应发送成功");
    66. }
    67. }
    68. }

     1.3Client类

    1. import java.io.*;
    2. import java.net.DatagramPacket;
    3. import java.net.DatagramSocket;
    4. import java.net.InetAddress;
    5. import java.util.Scanner;
    6. public class Client {
    7. public static void main(String[] args) throws IOException {
    8. DatagramSocket socket = new DatagramSocket(9999);
    9. // 目前服务器在本机: 127.0.0.1
    10. // 目前服务器的端口是: 8080
    11. Scanner sc = new Scanner(System.in);
    12. while (sc.hasNextLine()) {
    13. String word = sc.nextLine();
    14. // 准备发送请求
    15. String request = "我想进行翻译工作\r\n" + word + "\r\n";
    16. byte[] bytes = request.getBytes("UTF-8");
    17. DatagramPacket sent = new DatagramPacket(
    18. bytes,
    19. 0,
    20. bytes.length,
    21. InetAddress.getLoopbackAddress(),
    22. 8080
    23. );
    24. socket.send(sent);
    25. byte[] buf = new byte[1024];
    26. DatagramPacket received = new DatagramPacket(buf, buf.length);
    27. socket.receive(received);
    28. int n = received.getLength();
    29. String response = new String(buf, 0, n, "UTF-8");
    30. System.out.println(response);
    31. }
    32. }
    33. }

    1.4Log类

    1. import java.text.DateFormat;
    2. import java.text.SimpleDateFormat;
    3. import java.util.Date;
    4. public class Log {
    5. public static void println(Object o) {
    6. Date date = new Date();
    7. DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    8. String now = format.format(date);
    9. System.out.println(now + ": " + o.toString());
    10. }
    11. public static void print(Object o) {
    12. Date date = new Date();
    13. DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    14. String now = format.format(date);
    15. System.out.print(now + ": " + o.toString());
    16. }
    17. public static void printf(String fmt, Object... args) {
    18. Date date = new Date();
    19. DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    20. String now = format.format(date);
    21. System.out.printf(now + ": " + fmt, args);
    22. }
    23. public static void main(String[] args) {
    24. print("Hello\r\n");
    25. println("你好");
    26. printf("%s %d", "Go", 13);
    27. }
    28. }

     

     

    2.数据报套接字

    使用传输层UDP协议(用户数据报协议),传输层协议。

    UDP: 无连接 不可靠传输 面向数据报 有接收缓冲区,无发送缓冲区 大小受限:一次最多传输64K 

    2.1java.net.ServerSocket类

    ServerSocket 是创建TCP服务端Socket的API

     

     java.net.ServerSocket类

    实现 同一主机下的进程 -> 进程 ---- 请求响应模型

    2.2Server类

    1. import java.io.*;
    2. import java.net.InetAddress;
    3. import java.net.ServerSocket;
    4. import java.net.Socket;
    5. import java.util.HashMap;
    6. import java.util.Scanner;
    7. public class Server {
    8. private static final HashMap<String, String> map = new HashMap<>();
    9. static {
    10. map.put("apple", "苹果");
    11. map.put("banana", "香蕉");
    12. }
    13. public static void main(String[] args) throws IOException {
    14. // 1. 创建 socket
    15. Log.println("服务器启动,监听在 TCP:8080 端口");
    16. ServerSocket serverSocket = new ServerSocket(8080);
    17. // 进行循环
    18. while (true) {
    19. // 1. 接通连接(电话) —— accept
    20. Log.println("等待新的客户端连接上来");
    21. Socket socket = serverSocket.accept(); // 阻塞
    22. Log.println("有新的客户端连接上来");
    23. InetAddress inetAddress = socket.getInetAddress();
    24. Log.println("对方的地址: " + inetAddress);
    25. int port = socket.getPort();
    26. // 由于没有设定,会随机分配一个端口
    27. Log.println("对方的端口: " + port);
    28. // is: 用于读数据
    29. InputStream is = socket.getInputStream();
    30. // os: 用于写数据
    31. OutputStream os = socket.getOutputStream();
    32. // 2. 读取请求
    33. Scanner scanner = new Scanner(is, "UTF-8");
    34. String header = scanner.nextLine();
    35. // TODO: 做请求格式检查
    36. String englishWord = scanner.nextLine();
    37. Log.println("英文单词: "+ englishWord);
    38. // 3. 处理业务
    39. String chineseWord = map.getOrDefault(englishWord, "不认识");
    40. Log.println("中文单词: "+ chineseWord);
    41. // 4. 发送响应
    42. // 好的\r\n苹果\r\n
    43. // OutputStream封装 -> OutputStreamWriter封装 -> PrintWriter
    44. OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
    45. PrintWriter printWriter = new PrintWriter(writer);
    46. Log.println("服务器进行发送");
    47. printWriter.printf("好的\r\n%s\r\n", chineseWord);
    48. printWriter.flush();
    49. Log.println("服务器发送成功");
    50. socket.close();
    51. }
    52. }
    53. }

    2.3Client类

    1. import java.io.*;
    2. import java.net.Socket;
    3. import java.util.Scanner;
    4. public class Client {
    5. public static void main(String[] args) throws IOException {
    6. Socket socket = new Socket("127.0.0.1", 8080);
    7. InputStream is = socket.getInputStream();
    8. Scanner scanner = new Scanner(is, "UTF-8");
    9. OutputStream os = socket.getOutputStream();
    10. OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
    11. PrintWriter printWriter = new PrintWriter(writer);
    12. // 发送请求
    13. printWriter.printf("我想进行翻译工作\r\napple\r\n");
    14. printWriter.flush();
    15. // 读取响应
    16. String header = scanner.nextLine(); // 好的
    17. String word = scanner.nextLine(); // 苹果
    18. System.out.println(word);
    19. socket.close();
    20. }
    21. }

    2.4Log类

    1. import java.text.DateFormat;
    2. import java.text.SimpleDateFormat;
    3. import java.util.Date;
    4. public class Log {
    5. public static void println(Object o) {
    6. Date date = new Date();
    7. DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    8. String now = format.format(date);
    9. System.out.println(now + ": " + o.toString());
    10. }
    11. public static void print(Object o) {
    12. Date date = new Date();
    13. DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    14. String now = format.format(date);
    15. System.out.print(now + ": " + o.toString());
    16. }
    17. public static void printf(String fmt, Object... args) {
    18. Date date = new Date();
    19. DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    20. String now = format.format(date);
    21. System.out.printf(now + ": " + fmt, args);
    22. }
    23. public static void main(String[] args) {
    24. print("Hello\r\n");
    25. println("你好");
    26. printf("%s %d", "Go", 13);
    27. }
    28. }

     

    3.端口被占用问题

    此时需要检查进程B绑定的是哪个端口,再查看该端口被哪个进程占用

    3.1寻找端口方法1

    3.2寻找端口方法2

    3.3解决方法

    如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B

    如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口

    4.TCP的长连接和短连接

     TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

    短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。

    长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以 多次收发数据。

    4.1短连接-Client

    1. import java.io.*;
    2. import java.net.Socket;
    3. import java.util.Scanner;
    4. public class Client {
    5. public static void main(String[] args) throws IOException {
    6. while (true) {
    7. Socket socket = new Socket("127.0.0.1", 8080); // 拨号
    8. InputStream is = socket.getInputStream();
    9. Scanner scanner = new Scanner(is, "UTF-8");
    10. OutputStream os = socket.getOutputStream();
    11. OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
    12. PrintWriter printWriter = new PrintWriter(writer);
    13. // 发送请求
    14. printWriter.printf("我是Java19班的\r\napple\r\n");
    15. printWriter.flush();
    16. // 读取响应
    17. String header = scanner.nextLine(); // 好的
    18. String word = scanner.nextLine(); // 苹果
    19. System.out.println(word);
    20. socket.close(); // 挂断电话
    21. }
    22. }
    23. }

    4.2长连接---Client

    1. import java.io.*;
    2. import java.net.Socket;
    3. import java.util.Scanner;
    4. public class Client {
    5. public static void main(String[] args) throws IOException {
    6. Socket socket = new Socket("127.0.0.1", 8080); // 拨号
    7. for (int i = 0; i < 3; i++) {
    8. InputStream is = socket.getInputStream();
    9. Scanner scanner = new Scanner(is, "UTF-8");
    10. OutputStream os = socket.getOutputStream();
    11. OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
    12. PrintWriter printWriter = new PrintWriter(writer);
    13. // 发送请求
    14. printWriter.printf("我是Java19班的\r\napple\r\n");
    15. printWriter.flush();
    16. // 读取响应
    17. String header = scanner.nextLine(); // 好的
    18. String word = scanner.nextLine(); // 苹果
    19. System.out.println(word);
    20. }
    21. //socket.close(); // 挂断电话
    22. }
    23. }

     4.3长连接---Server

    1. import java.io.*;
    2. import java.net.InetAddress;
    3. import java.net.ServerSocket;
    4. import java.net.Socket;
    5. import java.util.HashMap;
    6. import java.util.Scanner;
    7. public class Server {
    8. private static final HashMap<String, String> map = new HashMap<>();
    9. static {
    10. map.put("apple", "苹果");
    11. map.put("banana", "香蕉");
    12. }
    13. public static void main(String[] args) throws IOException {
    14. // 1. 创建 socket
    15. Log.println("服务器启动,监听在 TCP:8080 端口");
    16. ServerSocket serverSocket = new ServerSocket(8080);
    17. // 进行循环
    18. while (true) {
    19. // 1. 接通连接(电话) —— accept
    20. Log.println("等待新的客户端连接上来");
    21. Socket socket = serverSocket.accept(); // 阻塞
    22. Log.println("有新的客户端连接上来");
    23. InetAddress inetAddress = socket.getInetAddress();
    24. Log.println("对方的地址: " + inetAddress);
    25. int port = socket.getPort();
    26. Log.println("对方的端口: " + port);
    27. // is: 用于读数据
    28. InputStream is = socket.getInputStream();
    29. Scanner scanner = new Scanner(is, "UTF-8");
    30. // os: 用于写数据
    31. OutputStream os = socket.getOutputStream();
    32. // OutputStream -> OutputStreamWriter -> PrintWriter
    33. OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
    34. PrintWriter printWriter = new PrintWriter(writer);
    35. // client 挂断电话,表示本次连接的请求响应处理结束了
    36. // client 通过关闭连接的方式来体现
    37. // is 读到了 EOS(-1)
    38. // scanner hasNextLine()
    39. while (scanner.hasNextLine()) {
    40. // 2. 读取请求
    41. String header = scanner.nextLine(); // "我是 Java19班的" // 阻塞
    42. // TODO: 做请求格式检查
    43. String englishWord = scanner.nextLine();
    44. Log.println("英文单词: " + englishWord);
    45. // 3. 处理业务
    46. String chineseWord = map.getOrDefault(englishWord, "不认识");
    47. Log.println("中文单词: " + chineseWord);
    48. // 4. 发送响应
    49. // 好的\r\n苹果\r\n
    50. Log.println("服务器进行发送");
    51. printWriter.printf("好的\r\n%s\r\n", chineseWord);
    52. printWriter.flush(); // 不要忘记 flush
    53. Log.println("服务器发送成功");
    54. }
    55. socket.close();
    56. }
    57. }
    58. }

    4.4长连接---Client(用户输入)

    1. import java.io.*;
    2. import java.net.Socket;
    3. import java.util.Scanner;
    4. public class Client {
    5. public static void main(String[] args) throws IOException {
    6. Log.println("准备建立连接");
    7. Socket socket = new Socket("182.254.132.183", 8080); // 拨号
    8. Log.println("连接已经建立");
    9. Scanner systemInScanner = new Scanner(System.in);
    10. while (systemInScanner.hasNextLine()) {
    11. String w = systemInScanner.nextLine();
    12. InputStream is = socket.getInputStream();
    13. Scanner socketScanner = new Scanner(is, "UTF-8");
    14. OutputStream os = socket.getOutputStream();
    15. OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
    16. PrintWriter printWriter = new PrintWriter(writer);
    17. // 发送请求
    18. printWriter.printf("我是Java19班的\r\n%s\r\n", w);
    19. printWriter.flush();
    20. // 读取响应
    21. String header = socketScanner.nextLine(); // 好的
    22. String word = socketScanner.nextLine(); // 苹果
    23. System.out.println(word);
    24. }
    25. //socket.close(); // 挂断电话
    26. }
    27. }

    长连接模式下,由于TCP面向的是字节流

  • 相关阅读:
    全自主巡航无人机项目思路:STM32/PX4 + ROS + AI 实现从传感融合到智能规划的端到端解决方案
    【Javaweb 2】JSP,Filter,Listener,AJAX,Vue
    docker镜像如何下载到本地
    2024 ,Android 15 预览版来了
    Ubuntu中用useradd创建用户时没指定家目录和shell版本,就不能su切换到新用户
    【Python】-- 集合的常用方法
    【21天学习挑战赛】—Java编程进阶之路(3)
    算法训练Day42 | 01背包问题的理论基础(二维和一维dp数组的全面剖析);LeetCode416. 分割等和子集(01背包的应用)
    需求侧电力负荷预测(Matlab代码实现)
    【元宇宙欧米说】对话NFT平台丨Maze NFT 协议
  • 原文地址:https://blog.csdn.net/qq_46235384/article/details/124785582