Socket套接字(Java)

1.UDP数据报套接字

1.1.DatagramSocket API

DatagramSocket 是 UDP Socket,用于发送和接收UDP数据报(DatagramPacket).

构造方法

构造方法方法作用
无参( DatagramSocket() )创建一个UDP数据报套接字的Socket, 绑定到本机任意一个随机端口(一般用于客户端)
一个int型参数( DatagramSocket(int port) )创建一个UDP数据报套接字的Socket, 绑定到本机指定的端口(一般用于服务端)

常用方法

常用方法方法作用
void receive(DatagramPacket p)从此套接字接收数据报.接收到的数据报会存储到参数p中(如果没有接收到数据报, 该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报.(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

1.2.DatagramPacket API

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

构造方法

构造方法方法作用
DatagramPacket(byte[] buf,int length)构造一个DatagramPacket用来接收数据报, 第一个参数buf 用于接收数据(字节数组), 第二个参数指定接收最大长度 length
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address)构造一个DatagramPacket以用来发送数据报, 第一个参数buf为发送的数据(字节数组), 第二个和第三个参数用于指定长度,buf数组从offset到length用于存放接受到的数据.address中存放了一组IP和端口号(此处指目的主机的IP和端口号)

常用方法

常用方法方法作用
InetAddress getAddress()从接收到的数据报中,获取发送端主机的IP地址; 或从发送的数据报中, 获取接收端主机IP地址
int getPort()从接收到的数据报中,获取发送端主机的端口号; 或从发送的数据报中, 获取接收端主机的端口号
byte[] getData()获取数据报中存放的数据

1.3.InetSocketAddress API

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

构造方法

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

1.4.根据 UDP Socket 模拟回显服务器和客户端

回显服务器:原样返回接收到的数据,不进行逻辑运算.

Server

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

public class UDPEchoServer {
    //服务器Socket,用于接收和发送数据报
    private DatagramSocket socket = null;

    public UDPEchoServer(int port) throws SocketException {
        //port  端口号:启动时绑定端口号.用于区分应用程序的
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        //启动服务器
        System.out.println("启动服务器");
        while (true) {
            //1. 读取客户端发来的请求
            //构造数据报用于接收数据,第二个参数表示最大接收长度
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);
            //阻塞等待:接受数据
            socket.receive(requestPacket);

            String request = new String(requestPacket.getData(), 0, requestPacket.getLength(), "UTF-8");
            //2. 根据请求计算响应
            String response = process(request);
            //3.把响应写回给客户端
            //直接response.length()可能不准确(可能会小一些)
            //requestPacket.getSocketAddress() 返回一个类:包含IP和端口号
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress());
            //发送数据报
            socket.send(responsePacket);
        }
    }

    private String process(String request) {
        //回显逻辑:直接返回
        return request;
    }

    public static void main(String[] args) throws IOException {
        UDPEchoServer server = new UDPEchoServer(9090);
        server.start();
    }
}

Client

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class UDPEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;
    private int serverPort;

    public UDPEchoClient(String ip, int port) throws SocketException {
        //客户端不指定端口号,系统随机分配端口
        socket = new DatagramSocket();
        //此处端口为服务器端口
        this.serverIp = ip;
        this.serverPort = port;
    }

    public void start() throws IOException {
        System.out.println("client上线");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            //1.先从控制台读取用户输入的字符串
            System.out.println("client->");
            String request = scanner.nextLine();
            if ("exit".equals(request)) {
                //用于客户端下线
                break;
            }
            //2.把这个用户输入的内容,构造成一个UDP请求,并发送
            //分别构造 IP 和 端口
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp), serverPort);
            //发送数据报
            socket.send(requestPacket);
            //3.从服务器读取响应数据并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(), 0, requestPacket.getLength(), "UTF-8");
            //4.把响应结果显示到控制台上.
            System.out.println("server->" + response);
            System.out.println();
        }
        System.out.println("client下线");
    }

    public static void main(String[] args) throws IOException {
        UDPEchoClient client = new UDPEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

2.TCP数据报套接字

2.1.ServerSocket API

ServerSocket 是创建TCP服务端Socket的API

构造方法

构造方法方法作用
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

常用方法

常用方法方法作用
Socket accept()开始监听指定端口(创建时绑定的端口), 有客户端连接后, 返回一个服务端Socket 对象, 并基于该Socket建立与客户端的连接, 否则阻塞等待
void close()关闭此套接字

2.2.Socket API

Socket 是双方建立连接以后,保存双方信息.用于收发数据.

构造方法

构造方法方法作用
Socket(String host, int port)创建一个流套接字Socket,并与对应IP的主机上对应的端口进程建立连接

常用方法

常用方法方法作用
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流,用于获取对端的输入信息
OutputStream getOutputStream()返回此套接字的输出流,用于给对端输出信息

2.3.根据 TCP Socket 模拟回显服务器和客户端

由于TCP建立连接后会保持.所以这里使用线程池来解决多次建立连接的问题.

Server

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TCPThreadPoolEchoServer {
    private ServerSocket serverSocket = null;

    public TCPThreadPoolEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动~");
        //线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        while (true) {
            //阻塞等待建立连接:接到连接后交给其他线程通话.此线程继续监听.
            Socket clientSocket = serverSocket.accept();
            //建立链接之后互相通话
            pool.submit(() -> processConnection(clientSocket));
        }
    }

    private void processConnection(Socket clientSocket) {
        //此时通道已经建立.直接通过socket互相通话即可
        System.out.printf("[%s:%d] 客户端建立连接~\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream()) {
            try (OutputStream outputStream = clientSocket.getOutputStream()) {
                Scanner scanner = new Scanner(inputStream);
                while (true) {
                    //循环处理请求
                    if (!scanner.hasNext()) {
                        System.out.printf("[%s:%d] 客户端断开连接~\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                        break;
                    }
                    String request = scanner.next();
                    String response = process(request);
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(response);
                    //保证每次输入内容发送过去
                    printWriter.flush();

                    System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //该客户端断开连接时释放资源
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPThreadPoolEchoServer tcpEchoServer = new TCPThreadPoolEchoServer(10101);
        tcpEchoServer.start();
    }
}

Client

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class TCPThreadPoolEchoClient {
    private Socket socket = null;

    public TCPThreadPoolEchoClient(String serverIp, int port) throws IOException {
        //参数代表与 IP+端口 建立连接
        socket = new Socket(serverIp, port);
    }

    public void start() {
        System.out.println("和服务器连接成功");
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream()) {
            try (OutputStream outputStream = socket.getOutputStream()) {
                while (true) {
                    System.out.print("->");
                    String request = scanner.next();
                    if ("exit".equals(request)) {
                        break;
                    }
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(request);
                    printWriter.flush();

                    Scanner respScanner = new Scanner(inputStream);
                    String response = respScanner.next();

                    System.out.printf("req: %s, resp: %s\n", request, response);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TCPThreadPoolEchoClient tcpEchoClient = new TCPThreadPoolEchoClient("127.0.0.1", 10101);
        tcpEchoClient.start();
    }
}
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魚小飛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值