赞
踩
目录
API 介绍
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。
DatagramSocket 的构造方法:
方法签名 | 方法说明 |
DatagramSocket() | 创建⼀个UDP数据报套接字的Socket,绑定到本机任意⼀个随机端口(⼀般用于客户端) |
DatagramSocket(int port) | 创建⼀个UDP数据报套接字的Socket,绑定到本机指定的端口(⼀般用于服务端) |
DatagramSocket 的方法:
方法签名 | 方法说明 |
void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待) |
void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发 送) |
void close() | 关闭此数据报套接字 |
DatagramPacket 是UDP Socket发送和接收的数据报
DatagramPacket 构造方法:
方法签名 | 方法说明 |
DatagramPacket(byte[] buf, int length) | 构造⼀个DatagramPacket以⽤来接收数据报,接收的 数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length) |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) | 构造⼀个DatagramPacket以用来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定长度(第⼆个参数length)。address指定目的主机的IP 和端口号 |
DatagramPacket 方法:
方法签名 | 方法说明 |
InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 |
int getPort() | 从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号 |
byte[] getData() | 获取数据报中的数据 |
构造UDP发送的数据报时,需要传入SocketAddress ,该对象可以使用 InetSocketAddress 来创建。
InetSocketAddress ( SocketAddress 的子类 )构造方法:
方法签名 | 方法说明 |
InetSocketAddress(InetAddress addr, int port) | 创建⼀个Socket地址,包含IP地址和端⼝号 |
服务器端代码:
- import java.io.IOException;
- import java.net.DatagramPacket;
- import java.net.DatagramSocket;
- import java.net.SocketException;
- import java.text.SimpleDateFormat;
- import java.util.Calendar;
-
- public class UdpEchoServer {
- private DatagramSocket socket = null;
-
- public UdpEchoServer(int port) throws SocketException {
- socket = new DatagramSocket(port); //port:端口号
- }
-
- public void start() throws IOException {
- System.out.println("服务器启动!");
- while (true) {
- //每次循环,就是一个请求-响应过程
- //1.读取请求并解析
- DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
- socket.receive(requestPacket);
- //基于字节数组构造String
- String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
- //2.根据请求计算响应(对于回显服务器来说,这一步啥都不做)
- String response = process(request);
- //把响应返回客户端
- // 构造一个 DatagramPacke 作为相应对象
- DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
- 0, response.getBytes().length,
- requestPacket.getSocketAddress()); //得到 INetAddress 对象, 这个对象包含了 ip 和端口号
- socket.send(responsePacket);
-
- //打印日志
- Calendar calendar = Calendar.getInstance();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- String dateTime = sdf.format(calendar.getTime());
- System.out.printf("[%s:%d time:%s] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
- requestPacket.getPort(),dateTime, request, response);
- }
- }
-
- public String process(String request) {
- return request;
- }
-
- public static void main(String[] args) throws IOException {
- UdpEchoServer server = new UdpEchoServer(9090);
- server.start();
- }
- }

客户端代码
- import java.io.IOException;
- import java.net.*;
- import java.util.Scanner;
-
- public class UdpEchoClient {
- private DatagramSocket socket = null;
- private String serverIp;
- private int serverPort;
-
- public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
- this.serverIp = serverIp;
- this.serverPort = serverPort;
- socket = new DatagramSocket();
- }
-
- public void start() throws IOException {
- System.out.println("客户端启动!");
- Scanner scanner = new Scanner(System.in);
- while (true) {
- System.out.print("->"); //提示用户接下来要输入内容
- //1.从控制台读取要发送的数据
- if (!scanner.hasNext()) {
- break;
- }
- String request = scanner.next();
- //2.构造请求并发送
- DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
- InetAddress.getByName(serverIp), serverPort);
- socket.send(requestPacket);
- //3.读取服务器的响应
- DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
- socket.receive(responsePacket);
- //4.把响应显示到控制台上
- String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
- System.out.println(response);
- }
- }
-
- public static void main(String[] args) throws IOException {
- UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
- client.start();
- }
- }

运行效果:
编写⼀个英译汉的服务器. 只需要重写 process
- import java.io.IOException;
- import java.net.SocketException;
- import java.util.HashMap;
-
- //词典服务器
- public class UdpDictServer extends UdpEchoServer {
- private HashMap<String, String> hashMap = new HashMap<>();
-
- public UdpDictServer(int port) throws SocketException {
- super(port);
- hashMap.put("cat", "小猫");
- hashMap.put("dog", "小狗");
- hashMap.put("chicken", "坤坤");
- hashMap.put("aaaaaaaaaa", "测试");
- }
-
- //start 方法完全从父类中继承下来即可
- //process 方法要进行重写,加入另外的业务逻辑,进行翻译
- @Override
- public String process(String request) {
- return hashMap.getOrDefault(request, "您查的单词不存在!");
- }
-
- public static void main(String[] args) throws IOException {
- UdpDictServer server = new UdpDictServer(9090);
- server.start();
- }
- }

运行效果
API 介绍
ServerSocket 是创建TCP服务端Socket的API。
ServerSocket 构造方法:
方法签名 | 方法说明 |
ServerSocket(int port) | 创建⼀个服务端流套接字Socket,并绑定到指定端⼝ |
ServerSocket 方法:
方法签名 | 方法说明 |
Socket accept() | 开始监听指定端⼝(创建时绑定的端⼝),有客户端 连接后,返回⼀个服务端Socket对象,并基于该 Socket建立与客户端的连接,否则阻塞等待 |
void close() | 关闭此套接字 |
Socket 是客户端 Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服 务端Socket。
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。
Socket 构造方法:
方法签名 | 方法说明 |
Socket(String host, int port) | 创建⼀个客户端流套接字Socket,并与对应IP的主机 上,对应端口的进程建立连接 |
Socket 方法:
方法签名 | 方法说明 |
InetAddress getInetAddress() | 返回套接字所连接的地址 |
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
服务器端代码:
- import java.io.*;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Scanner;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
-
- public class TcpEchoServer {
- private ServerSocket serverSocket = null;
-
- public TcpEchoServer(int port) throws IOException {
- serverSocket = new ServerSocket(port);
- }
-
- public void start() throws IOException {
- System.out.println("服务器启动!");
- // 创建线程池
- ExecutorService pool = Executors.newCachedThreadPool();
- while(true) {
- // 通过accept方法"接听电话",然后才能进行通信
- Socket clientSocket = serverSocket.accept();
- pool.submit(new Runnable() {
- @Override
- public void run() {
- processConnection(clientSocket);
- }
- });
- }
- }
-
- //通过这个方法来处理一次连接, 连接建立过程中涉及到多次请求的响应交互
- private void processConnection(Socket clientSocket) {
- System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
- //循环的读取客户端的请求并返回响应
- try(InputStream inputStream = clientSocket.getInputStream();
- OutputStream outputStream = clientSocket.getOutputStream()) {
- Scanner scanner = new Scanner(inputStream);
- while(true) {
- if(!scanner.hasNext()) {
- //读取完毕,客户端断开连接,就会产生读取完毕
- System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
- break;
- }
- //1.读取请求并解析 next要读到空白符才会结束
- String request = scanner.next();
- //2.根据请求计算响应
- String response = process(request);
- //3.把响应返回给客户端
- //通过这种方法可以写回, 但是这种方法不方便给返回的响应中添加\n
- //outputStream.write(response.getBytes(),0,response.getBytes().length);
- //也可以给 outputStream 套上一层,完成更方便的写入
- PrintWriter printWriter = new PrintWriter(outputStream);
- printWriter.println(response);
- printWriter.flush();
-
- System.out.printf("[%s:%d] req: %s, resp: %s \n",clientSocket.getInetAddress(),clientSocket.getPort(),
- request,response);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- try {
- clientSocket.close();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- public String process(String request) {
- return request;
- }
-
- public static void main(String[] args) throws IOException {
- TcpEchoServer server = new TcpEchoServer(9090);
- server.start();
- }
- }

注意: 如果只是单个线程, 无法同时响应多个客户端. 此处给每个客户端都分配⼀个线程. 为了避免频繁创建销毁线程, 引入线程池
客户端代码:
- 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 TcpEchoClient {
- private Socket socket = null;
-
- public TcpEchoClient(String serverIp,int serverPort) throws IOException {
- //此处可以把这里的ip和port直接传给socket对象
- //由于tcp是有连接的,因此 socket 里面就会保存这两信息
- socket = new Socket(serverIp,serverPort);
- }
-
- public void start() {
- System.out.println("客户端启动");
-
- try(InputStream inputStream = socket.getInputStream();
- OutputStream outputStream = socket.getOutputStream()) {
- Scanner scannerConsole = new Scanner(System.in);
- Scanner scannerNetWork = new Scanner(inputStream);
- PrintWriter writer = new PrintWriter(outputStream);
- while(true) {
- //这里的流程和 UDP 的客户端类似
- //1.从控制台读取输入的字符串
- System.out.print("-> ");
- if(!scannerConsole.hasNext()) {
- break;
- }
- String request = scannerConsole.next();
- //2.把请求发给服务器, 这里需要使用println来发送, 为了让发送的请求末尾带有 \n
- writer.println(request);
- // 通过flush刷新缓冲区
- writer.flush();
- //3.从服务器读取响应
- String response = scannerNetWork.next();
- //4.把响应显示出来
- System.out.println(response);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static void main(String[] args) throws IOException {
- TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
- client.start();
- }
- }

使用PrintWrite记得手动刷新缓冲区:
运行效果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。