什么是Socket


Socket 是一种网络协议,可以让服务器和客户端保持互联,常用于通讯软件的协议。

Socket实例


建立连接的语法非常简单,如下:

Socket socket = new Socket(host, port);

当然,Socket也可以获取流,并且支持 try_catch_resource

try (Socket socket = new Socket("bbs.newsmth.net", 23)) {  
    InputStream is = socket.getInputStream();  
    Scanner scanner = new Scanner(is, "gbk");  
  
    while (scanner.hasNextLine()) {  
        String line = scanner.nextLine();  
        System.out.println(line);  
    }  
  
} catch (UnknownHostException e) {  
    throw new RuntimeException(e);  
} catch (IOException e) {  
    throw new RuntimeException(e);  
}

ServerSocket实例


ServerSocket 就是 Socket 的服务端(数据响应),代码示例如下:

try(ServerSocket server = new ServerSocket(8881);  
    Socket socket = server.accept();  
    InputStream is = socket.getInputStream();  
    OutputStream os = socket.getOutputStream();  
  
    Scanner scanner = new Scanner(is)){  
    PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);  
    pw.println("SOCKET 200 OK,输入【exit】退出");  
  
    boolean done = false;  
    while(!done && scanner.hasNextLine()) {  
        String line = scanner.nextLine();  
        System.out.println(line);  
        if ("exit".equals(line)) done = true;  
    }  
} catch (IOException e) {  
    throw new RuntimeException(e);  
}

其中的

Socket socket = server.accept()
  • accept 方法:ServerSocket 的 accept 方法是一个阻塞式方法 。这意味着当执行到这行代码时,服务器端程序会暂停在此处,持续监听指定端口,直到有客户端尝试连接到这个端口。一旦检测到有客户端连接请求,它就会接受这个连接请求,并返回一个和客户端通信的 Socket 对象。

启动 ServerSocket 后,可以使用 telnet 测试

telnet 127.0.0.1 8881

结果如下:

多个客户端

Socket默认其实只支持一对一服务,如果想要接受多个Socket请求的话,可以启用一个进程来处理。

模板:

try (ServerSocket server = new ServerSocket(8881)) {  
    while (true) {  
        Socket socket = server.accept();  
        Thread thread = new Thread(new Runnable() {  
            @Override  
            public void run() {  
                // 套接字处理程序  
            }  
        });  
        thread.start();  
    }  
} catch (IOException e) {  
    e.printStackTrace();  
}

使用示例:

try (ServerSocket server = new ServerSocket(8881)) {  
    while (true) {  
        Socket socket = server.accept();  
        Thread thread = new Thread(() -> {  
            try (InputStream is = socket.getInputStream();  
                 OutputStream os = socket.getOutputStream();  
                 Scanner scanner = new Scanner(is)  
            ) {  
                PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);  
                pw.println("SOCKET 200 OK,输入【exit】退出");  
                boolean done = false;  
                while (!done && scanner.hasNextLine()) {  
                    String line = scanner.nextLine();  
                    System.out.println("接受到来自端口: "+socket.getPort() + " 的信息\t" +line);  
                    if ("exit".equals(line)) done = true;  
  
                }  
            } catch (IOException e) {  
                throw new RuntimeException(e);  
            }  
        });  
        thread.start();  
    }  
} catch (IOException e) {  
    e.printStackTrace();  
}

测试:

多线程开发


关于线程:多线程

刚刚上面的一对多关系其实就是使用多线程,以下示例将会自定义ClientHandler 辅助开发,同时也会搭建客户端

Server

public class MultiThreadedServer {  
    public static void main(String[] args) throws IOException {  
        int port = 12345;  
        ServerSocket server = new ServerSocket(port);  
        System.out.println("Server is listening on port: " + port);  
  
        while (true) {  
            Socket socket = server.accept();  
            System.out.println("Client connected.");  
            new ClientHandler(socket).start();  
        }  
    }  
  
}  
  
class ClientHandler extends Thread {  
    private final Socket socket;  
  
    public ClientHandler(Socket socket) {  
        this.socket = socket;  
    }  
  
    @Override  
    public void run() {  
        try (  
                InputStream is = socket.getInputStream();  
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));  
  
                OutputStream os = socket.getOutputStream();  
                PrintWriter writer = new PrintWriter(os, true)  
  
        ) {  
            String line;  
            while ((line = reader.readLine()) != null) {  
                System.out.println("Reviced: " + line);  
                writer.println("Server: " + line);  
            }  
            socket.close();  
  
        } catch (IOException e) {  
            throw new RuntimeException(e);  
        }  
    }  
}

client:

public class Client {  
    public static void main(String[] args) throws IOException {  
        String hostname = "localhost";  
        int port = 12345;  
  
        Socket socket = new Socket(hostname, port);  
        System.out.println("Connected to server: " + socket);  
  
        InputStream is = socket.getInputStream();  
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));  
  
        OutputStream os = socket.getOutputStream();  
        PrintWriter writer = new PrintWriter(os, true);  
  
        writer.println("Hello, server!");  
        String response = reader.readLine();  
        System.out.println("Server response: " + response);  
  
        socket.close();  
  
    }  
}

DatagramSocket实例


DatagramSocket 是 Java 中实现 UDP协议的核心类,可以使用 UDP 协议为Socket通信。

服务器端代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServer {
    public static void main(String[] args) throws IOException {
        int port = 12345;
        DatagramSocket serverSocket = new DatagramSocket(port);
        System.out.println("Server is listening on port " + port);

        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        serverSocket.receive(packet);
        String message = new String(packet.getData(), 0, packet.getLength());
        System.out.println("Received: " + message);

        serverSocket.close();
    }
}

客户端代码:

import java.io.IOException;
import java.net.*;

public class UDPClient {
    public static void main(String[] args) throws IOException {
        String hostname = "localhost";
        int port = 12345;

        InetAddress address = InetAddress.getByName(hostname);
        DatagramSocket clientSocket = new DatagramSocket();

        String message = "Hello, server!";
        byte[] buffer = message.getBytes();

        DatagramPacket packet = new DatagramPacket(buffer, buffer.length, address, port);
        clientSocket.send(packet);
        System.out.println("Message sent");

        clientSocket.close();
    }
}

在这个示例中,服务器端创建一个 DatagramSocket 对象并监听端口 12345。然后,它创建一个 DatagramPacket 对象,用于存储接收到的数据包。serverSocket.receive(packet) 方法阻塞,直到收到一个数据包。收到数据包后,服务器从数据包中提取并打印消息。

客户端首先解析服务器的 IP 地址,然后创建一个 DatagramSocket 对象。接着,客户端创建一个包含要发送消息的 DatagramPacket 对象,并指定目标地址和端口。最后,客户端通过调用 clientSocket.send(packet) 方法发送数据包。