【JavaEE】网络原理:UDP数据报套接字编程和TCP流套接字编程

2024-01-28 05:52

本文主要是介绍【JavaEE】网络原理:UDP数据报套接字编程和TCP流套接字编程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.UDP数据报套接字编程

1.1 DatagramSocket

1.2 DatagramPacket

1.3 InetSocketAddress

1.4 基于UDP实现回响服务器

2.TCP流套接字编程

2.1 ServerSocket

2.2 Socket

2.3 基于TCP实现回响服务器


1.UDP数据报套接字编程

API 介绍

1.1 DatagramSocket

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

DatagramSocket 的构造方法:

方法签名方法说明
DatagramSocket()创建⼀个UDP数据报套接字的Socket,绑定到本机任意⼀个随机端口(⼀般用于客户端)
DatagramSocket(int port)创建⼀个UDP数据报套接字的Socket,绑定到本机指定的端口(⼀般用于服务端)

DatagramSocket 的方法:

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发 送)
void close()关闭此数据报套接字

1.2 DatagramPacket

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 来创建。

1.3 InetSocketAddress

InetSocketAddress ( SocketAddress 的子类 )构造方法:

方法签名方法说明
InetSocketAddress(InetAddress addr, int port)创建⼀个Socket地址,包含IP地址和端⼝号

1.4 基于UDP实现回响服务器

服务器端代码:

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);//基于字节数组构造StringString 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 方法要进行重写,加入另外的业务逻辑,进行翻译@Overridepublic String process(String request) {return hashMap.getOrDefault(request, "您查的单词不存在!");}public static void main(String[] args) throws IOException {UdpDictServer server = new UdpDictServer(9090);server.start();}
}

 运行效果

2.TCP流套接字编程

API 介绍

2.1 ServerSocket

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

ServerSocket 构造方法:

方法签名方法说明
ServerSocket(int port)创建⼀个服务端流套接字Socket,并绑定到指定端⼝

ServerSocket 方法

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

2.2 Socket

Socket 是客户端 Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服 务端Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket 构造方法:

方法签名方法说明
Socket(String host, int port)创建⼀个客户端流套接字Socket,并与对应IP的主机 上,对应端口的进程建立连接

Socket 方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

2.3 基于TCP实现回响服务器

服务器端代码:

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() {@Overridepublic 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来发送, 为了让发送的请求末尾带有 \nwriter.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记得手动刷新缓冲区:

 运行效果

这篇关于【JavaEE】网络原理:UDP数据报套接字编程和TCP流套接字编程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/652659

相关文章

Python在二进制文件中进行数据搜索的实战指南

《Python在二进制文件中进行数据搜索的实战指南》在二进制文件中搜索特定数据是编程中常见的任务,尤其在日志分析、程序调试和二进制数据处理中尤为重要,下面我们就来看看如何使用Python实现这一功能吧... 目录简介1. 二进制文件搜索概述2. python二进制模式文件读取(rb)2.1 二进制模式与文本

Python中Tkinter GUI编程详细教程

《Python中TkinterGUI编程详细教程》Tkinter作为Python编程语言中构建GUI的一个重要组件,其教程对于任何希望将Python应用到实际编程中的开发者来说都是宝贵的资源,这篇文... 目录前言1. Tkinter 简介2. 第一个 Tkinter 程序3. 窗口和基础组件3.1 创建窗

基于C++的UDP网络通信系统设计与实现详解

《基于C++的UDP网络通信系统设计与实现详解》在网络编程领域,UDP作为一种无连接的传输层协议,以其高效、低延迟的特性在实时性要求高的应用场景中占据重要地位,下面我们就来看看如何从零开始构建一个完整... 目录前言一、UDP服务器UdpServer.hpp1.1 基本框架设计1.2 初始化函数Init详解

Java中Map的五种遍历方式实现与对比

《Java中Map的五种遍历方式实现与对比》其实Map遍历藏着多种玩法,有的优雅简洁,有的性能拉满,今天咱们盘一盘这些进阶偏基础的遍历方式,告别重复又臃肿的代码,感兴趣的小伙伴可以了解下... 目录一、先搞懂:Map遍历的核心目标二、几种遍历方式的对比1. 传统EntrySet遍历(最通用)2. Lambd

Spring Boot 中 RestTemplate 的核心用法指南

《SpringBoot中RestTemplate的核心用法指南》本文详细介绍了RestTemplate的使用,包括基础用法、进阶配置技巧、实战案例以及最佳实践建议,通过一个腾讯地图路线规划的案... 目录一、环境准备二、基础用法全解析1. GET 请求的三种姿势2. POST 请求深度实践三、进阶配置技巧1

springboot+redis实现订单过期(超时取消)功能的方法详解

《springboot+redis实现订单过期(超时取消)功能的方法详解》在SpringBoot中使用Redis实现订单过期(超时取消)功能,有多种成熟方案,本文为大家整理了几个详细方法,文中的示例代... 目录一、Redis键过期回调方案(推荐)1. 配置Redis监听器2. 监听键过期事件3. Redi

Spring Boot 处理带文件表单的方式汇总

《SpringBoot处理带文件表单的方式汇总》本文详细介绍了六种处理文件上传的方式,包括@RequestParam、@RequestPart、@ModelAttribute、@ModelAttr... 目录方式 1:@RequestParam接收文件后端代码前端代码特点方式 2:@RequestPart接

SpringBoot整合Zuul全过程

《SpringBoot整合Zuul全过程》Zuul网关是微服务架构中的重要组件,具备统一入口、鉴权校验、动态路由等功能,它通过配置文件进行灵活的路由和过滤器设置,支持Hystrix进行容错处理,还提供... 目录Zuul网关的作用Zuul网关的应用1、网关访问方式2、网关依赖注入3、网关启动器4、网关全局变

SpringBoot全局异常拦截与自定义错误页面实现过程解读

《SpringBoot全局异常拦截与自定义错误页面实现过程解读》本文介绍了SpringBoot中全局异常拦截与自定义错误页面的实现方法,包括异常的分类、SpringBoot默认异常处理机制、全局异常拦... 目录一、引言二、Spring Boot异常处理基础2.1 异常的分类2.2 Spring Boot默

基于SpringBoot实现分布式锁的三种方法

《基于SpringBoot实现分布式锁的三种方法》这篇文章主要为大家详细介绍了基于SpringBoot实现分布式锁的三种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、基于Redis原生命令实现分布式锁1. 基础版Redis分布式锁2. 可重入锁实现二、使用Redisso