• 网络编程之Socket套接字



    提示:以下是本篇文章正文内容,Java系列学习将会持续更新

    一、什么是网络编程

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

    1-1 请求和响应

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

    1. 第一次:请求数据的发送
    2. 第二次:响应数据的发送。

    1-2 发送端和接收端

    在一次网络数据传输时:

    发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
    接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
    收发端:发送端和接收端两端,也简称为收发端。

    1-3 客户端和服务端

    1. 客户端先发送请求到服务端
    2. 服务端根据请求数据,执行相应的业务处理
    3. 服务端返回响应:发送业务处理结果
    4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

    请添加图片描述

    回到目录…

    二、Socket套接字

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

    2-1 UDP数据报套接字

    2-1-1 DatagramSocket

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

    构造方法方法说明
    DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
    DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)
    普通方法方法说明
    void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
    void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
    void close()关闭此数据报套接字

    2-1-2 DatagramPacket

    DatagramPacket是UDP Socket发送和接收的数据报。

    构造方法方法说明
    DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
    DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号
    普通方法方法说明
    InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
    int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
    byte[] getData()获取数据报中的数据

    构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。

    2-1-3 InetSocketAddress

    InetSocketAddressSocketAddress 的子类。

    构造方法方法说明
    InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

    回到目录…

    2-1-4 代码案例

    服务端:

    package day1.udp.mine;
    
    import day1.udp.dictionary_service.Log;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.util.Arrays;
    
    public class UdpServer {
    
        // 服务器要绑定的端口
        private static final int PORT = 8080;
    
        public static void main(String[] args) throws IOException {
    
            // 1.创建服务端DatagramSocket,指定端口,可以发送及接收UDP数据报
            DatagramSocket socket = new DatagramSocket(PORT);
            System.out.println("DEBUG: 服务器 bind 在 8080 端口上");
    
            // 2.创建数据报,用于接收客户端发送的数据
            byte[] buf = new byte[1024];
            // 构造一个 DatagramPacket 以用来接收数据报,接收的数据保存在字节数组
            DatagramPacket received = new DatagramPacket(buf, buf.length);
    
            System.out.println("DEBUG: 服务器准备接收数据");
            // 3.等待接收客户端发送的UDP数据报,该方法在接收到数据报之前会一直阻塞
            socket.receive(received);
            System.out.println("DEBUG: 服务器接收到了数据");
    
            // 对方的地址(ip)
            InetAddress address = received.getAddress();
            System.out.println("DEBUG: 对方的地址为: " + address);
            // 对方的端口(port)
            int port = received.getPort();
            System.out.println("DEBUG: 对方的端口为: " + port);
            // 真正收到的数据长度
            int n = received.getLength();
            System.out.println("DEBUG: 数据长度为: " + n);
            // 真正有用的数据
            byte[] actualData = Arrays.copyOf(buf, n);
            // byte[] + 字符集编码 -> String
            String request = new String(actualData, 0, actualData.length, "UTF-8");
            System.out.println("DEBUG: 解析到的请求为: " + request);
    
            // 处理请求
            System.out.println("DEBUG: 开始处理请求数据");
            String response = "我们已经对 " + request + " 请求,作出了响应。";
            // String + 字符集编码 -> byte[]
            byte[] bytes = response.getBytes("UTF-8");
            System.out.println("DEBUG: 数据处理完毕");
    
            // 发送响应
            System.out.println("DEBUG: 准备发送响应");
            DatagramPacket sent = new DatagramPacket(
                    bytes, 0, bytes.length,
                    address,
                    port
            );
            socket.send(sent);
            System.out.println("DEBUG: 响应发送成功");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    回到目录…

    客户端:

    package day1.udp.mine;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.util.Scanner;
    
    public class UdpClient {
        public static void main(String[] args) throws IOException {
    
            Scanner input = new Scanner(System.in);
            System.out.println("DEBUG: 请输入请求数据");
            String request = input.nextLine();
    
            System.out.println("DEBUG: 将请求数据打包");
            byte[] bytes = request.getBytes("UTF-8");
    
            System.out.println("DEBUG: 准备发送请求");
            DatagramPacket sent = new DatagramPacket(
                    bytes,
                    0,
                    bytes.length,
                    InetAddress.getLoopbackAddress(), // 得到本机地址
                    8080
            );
            DatagramSocket socket = new DatagramSocket(9999);
            socket.send(sent);
            System.out.println("DEBUG: 成功发送请求");
    
            System.out.println("DEBUG: 准备接收响应");
            byte[] buf = new byte[1024];
            DatagramPacket received = new DatagramPacket(buf, buf.length);
    
            socket.receive(received);       // 服务器会阻塞
            int n = received.getLength();
            String response = new String(buf, 0, n, "UTF-8");
            System.out.println("DEBUG: 收到响应");
            System.out.println(response);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    回到目录…

    2-2 TCP流套接字

    2-2-1 ServerSocket

    ServerSocket 是创建TCP服务端Socket的API。

    构造方法方法说明
    ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口
    普通方法方法说明
    Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
    void close()关闭此套接字

    2-2-2 Socket

    Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。
    不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

    构造方法方法说明
    Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接
    普通方法方法说明
    InetAddress getInetAddress()返回套接字所连接的地址
    InputStream getInputStream()返回此套接字的输入流
    OutputStream getOutputStream()返回此套接字的输出流

    回到目录…

    2-2-3 代码案例

    服务端:

    package day2.dict_service.tcp.mine;
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class TcpServer {
        //服务器socket要绑定固定的端口
        private static final int PORT = 8888;
    
        public static void main(String[] args) throws IOException {
    
            // 1.创建一个服务端ServerSocket,用于收发TCP报文
            ServerSocket server = new ServerSocket(PORT);
            System.out.println("DEBUG: 服务器 bind 在 8080 端口上");
    
            // 2.等待客户端连接,注意该方法为阻塞方法
            System.out.println("DEBUG: 等待客户端建立TCP连接...");
            Socket client = server.accept();
    
            System.out.println("DEBUG: 连接成功");
            System.out.printf("DEBUG: 客户端IP:%s%n", client.getInetAddress().getHostAddress());
            System.out.printf("DEBUG: 客户端端口号:%s%n", client.getPort());
    
            // 5.接收客户端的数据,需要从客户端Socket中的输入流获取
            System.out.println("接收到客户端请求:");
            InputStream is = client.getInputStream();
    
            // 为了方便获取字符串内容,可以将以上字节流包装为字符流
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    
            String line;
            // 一直读取到流结束:TCP是基于流的数据传输,一定要客户端关闭Socket输出流才表示服务端接收IO输入流结束
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
    
            // 6.双方关闭连接:服务端是关闭客户端socket连接
            client.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    回到目录…

    客户端:

    package day2.dict_service.tcp.mine;
    
    import java.io.*;
    import java.net.Socket;
    import java.util.Scanner;
    
    public class TcpClient {
        //服务端IP或域名
        private static final String SERVER_HOST = "localhost";
    
        //服务端Socket进程的端口号
        private static final int SERVER_PORT = 8888;
    
        public static void main(String[] args) throws IOException {
    
            Scanner input = new Scanner(System.in);
    
            // 3.创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接
            Socket client = new Socket(SERVER_HOST, SERVER_PORT);
            System.out.println("DEBUG: 建立连接。。。");
    
            // 4.发送TCP数据,是通过socket中的输出流进行发送
            OutputStream os = client.getOutputStream();
            System.out.println("DEBUG: 准备发送数据");
    
            // 为了方便输出字符串作为发送的内容,可以将以上字节流包装为字符流
            PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
    
            // 4-1.发送数据:
            System.out.println("DEBUG: 请发送数据");
            pw.println(input.nextLine());
            // 4-2.有缓冲区的IO操作,真正传输数据,需要刷新缓冲区
            pw.flush();
            // 7.双方关闭连接:客户端关闭socket连接
            client.close();
            System.out.println("DEBUG: 关闭连接");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    回到目录…


    总结:
    提示:这里对文章进行总结:
    以上就是今天的学习内容,本文是Java网络编程的学习,学习如何进行网络编程,Socket套接字在UDP和TCP协议中的不同用法。之后的学习内容将持续更新!!!

  • 相关阅读:
    Tomcat部署及优化
    自学软件测试必备的英文单词【1500道加语法】
    基于django和爬虫的影视评价分析系统
    一文了解linux都有哪些常用命令
    vue学习总结
    Mysql索引特性(重要)
    Android 反射在Android开发中的巧妙使用基础篇(二)
    web渗透工具
    C++模板
    MySQL安装到最后一步 write configuration file 失败 怎么办?及后安装步骤
  • 原文地址:https://blog.csdn.net/qq15035899256/article/details/126063746