JavaEE-网络编程套接字(UDP/TCP)
下面写一个简单的UDP客户端服务器流程
思路:
对于服务器端:读取请求,并解析–> 根据解析出的请求,做出响应(这里是一个回显,)–>把响应写回客户端
对于客户端:从控制台读取用户输入的内容–>从控制台读取用户输入的内容–>从控制台读取用户输入的内容–>将其显示在屏幕上
全部代码如下:
服务器端:
package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
//UDP的回显服务器 客户端发出的请求是啥,服务器返回的响应就是啥
public class UdpEchoServer {private DatagramSocket socket=null;// 指定服务器的portpublic UdpEchoServer(int port) throws SocketException {socket=new DatagramSocket(port);}//指定一个方法启动服务器public void start() throws IOException {System.out.println("服务器开始启动");while(true){// 反复的, 长期的执行针对客户端请求处理的逻辑.// 一个服务器, 运行过程中, 要做的事情, 主要是三个核心环节.//服务器这里需要接收请求//1.读取请求,并解析DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);//解析String request=new String(requestPacket.getData(),0, requestPacket.getLength());//2.根据解析出的请求,做出响应(这里是一个回显,)String response=process(request);//3. 把响应写回客户端 此时需要告诉网卡,要发的内容是啥,发给谁//构造一个发送数据包DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//记录日志System.out.printf("[%s:%d] req: %s, resp: %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}//这里是一个回显,只需要返回这个字符串public String process(String request){return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer=new UdpEchoServer(9090);udpEchoServer.start();}
}
客户端:
package network;import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpEchoClient {//由于客户端的port是自动分配的,所以这里不会像服务器那样配置port//但是,客户端需要向服务器发送请求,所以,这里我们需要知道服务器的ip和portprivate DatagramSocket socket=null;private String serverIp;private int serverPort;//外部指定服务器的ip和portpublic UdpEchoClient(String ip,int port) throws SocketException {this.serverIp=ip;this.serverPort=port;//客户端的port是自动分配的socket=new DatagramSocket();}// 让这个客户端反复的从控制台读取用户输入的内容. 把这个内容构造成 UDP 请求, 发给服务器. 再读取服务器返回的 UDP 响应// 最终再显示在客户端的屏幕上.public void start() throws IOException {Scanner scanner=new Scanner(System.in);System.out.println("客户端开始启动");while(true){//1. 从控制台读取用户输入的内容System.out.println("->");String requset=scanner.next();//2.构造请求对象,发送给服务器DatagramPacket requsetPacket=new DatagramPacket(requset.getBytes(),requset.getBytes().length,InetAddress.getByName(serverIp),serverPort);socket.send(requsetPacket);//3.读取服务器的响应,并解析出其内容DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);String response=new String(responsePacket.getData(),0,responsePacket.getLength());//4 。将其显示在屏幕上System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient udpEchoClient=new UdpEchoClient("127.0.0.1",9090);//127.0.0.1 本机ipudpEchoClient.start();}
}
运行结果如下
对上述过程中的一些谈论和分析:
多个客户端向一个服务器发送请求
下面写一个简单的翻译服务器
重写的服务器端的代码如下:
package network;import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;public class UdpDictServer extends UdpEchoServer{//使用HashMap保存中英文翻译的键值对private Map<String,String> dict =new HashMap<>();//实现父类的构造方法public UdpDictServer(int port) throws SocketException {super(port);//一些原始的键值对dict.put("cat","猫");dict.put("dog","狗");dict.put("people","人");}//与原始的UdpEachServer相比,这里对于请求的处理过程是不一样的//重写process方法@Overridepublic String process(String request) {//找到对应的翻译,并返回//getOrDefault方法,找到key所对应的value值,如果没有找到,则返回defaultValue(即第二个参数)return dict.getOrDefault(request,"该词没有查询到");}public static void main(String[] args) throws IOException {UdpDictServer server=new UdpDictServer(9090);// start 不需要重新再写一遍了. 直接就复用了之前的 startserver.start();}
}
执行结果如下:
下面写一个基于TCP 的回显流程
思路:
服务器端:先从队列中拿到一个“连接”–> 读取请求并解析–>根据请求计算响应–>把响应写回给客户端
客户端:从控制台输入字符串–>把请求发送给服务器–>从服务器读取响应.–>把响应打印出来
全部代码如下:
服务器端代码:
package network;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;//基于TCP的回显服务器
public class TcpEachServer {private ServerSocket serverSocket=null;//绑定端口号public TcpEachServer(int port) throws IOException {serverSocket=new ServerSocket(port);}//启动服务器public void start() throws IOException {System.out.println("服务器开始启动");while(true){//从管理连接的队列中拿出一个“连接”出来Socket clientSocket=serverSocket.accept();//处理这个连接内的请求processConnection(clientSocket);}}//这个方法用来处理连接中的逻辑private void processConnection(Socket clientSocket) throws IOException {//日志System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());//下面开始读取请求,计算响应,返回响应 三步曲//Socket对象内部包含两种字节流对象InputStream和OutputStream,可以先把这两个对象流获// 取到,方便后续处理过程种的读写工作try(InputStream inputStream=clientSocket.getInputStream();OutputStream outputStream=clientSocket.getOutputStream()){//不同于UDP协议中的无连接,在客户端的一次连接过程中,可能涉及多次请求/响应过程//因此。这里使用一个while循环,直到该连接中的所有请求处理完毕while(true){//1,读取请求并解析Scanner scanner=new Scanner(inputStream);//hasNext的作用是,检测输入流中是否有结束输入的控制符,比如0x1A(EOF,Ctrl-Z)//用于检测一个连接是否结束if(!scanner.hasNext()){//一个连接处理完毕System.out.printf("[%s:%d] 客户端本次连接处理完毕,下线!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}// 这个代码暗含一个约定, 客户端发过来的请求, 得是文本数据, 同时, 还得带有空白符作为分割. (比如换行这种)//next():当输入到空白符结束String request=scanner.next();//2.根据请求计算响应String response=process(request);//3. 把响应写回客户端,把OutputStream用PrintWriter(此处的PrintWriter相当于Scanner)包裹一下,便于发送数据//将outputStream和PrintWriter关联起来PrintWriter writer=new PrintWriter(outputStream);//使用 PrintWriter 的 println 方法,打印到输出流中 把响应返回给客户端.//此处用 println, 而不是 print 就是为了在结尾加上 \n . 方便客户端读取响应, 使用 scanner.next 读取.writer.println(response);//这里还需要加一个 "刷新缓冲区" 操作.将缓冲区的数据强制输出,用于清空缓冲区writer.flush();//日志 记录当前的请求和响应System.out.printf("[%s:%d] req: %s,resp: %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}}//回显,只需要再返回这个字符串public String process(String requset){return requset;}public static void main(String[] args) throws IOException {TcpEachServer tcpEachServer=new TcpEachServer(9090);tcpEachServer.start();}
}
客户端代码:
package network;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;//服务器端的ip和portpublic TcpEchoClient(String serverIp,int serverPort) throws IOException {//这个new的动作完成后,完成了tcp的建立socket=new Socket(serverIp,serverPort);}public void start() throws IOException {System.out.println("客户端启动");Scanner scannerConsole=new Scanner(System.in);//Socket对象内部包含两种字节流对象InputStream和OutputStream,可以先把这两个对象流获// 取到,方便后续处理过程种的读写工作try(InputStream inputStream=socket.getInputStream();OutputStream outputStream=socket.getOutputStream()){while(true){//1.从控制台输入字符串System.out.println("-->");String request=scannerConsole.next();//2.把请求发送给服务器 需要对request进行包装,使用PrintWriterPrintWriter printWriter=new PrintWriter(outputStream);//使用 println 带上换行. 后续服务器读取请求, 就可以使用 scanner.next 来获取了printWriter.println(request);//发送请求printWriter.flush();//3.从服务器中接收响应Scanner scannerNetwork=new Scanner(inputStream);String response=scannerNetwork.next();//4.把响应打印出来System.out.println(response);}}}public static void main(String[] args) throws IOException {TcpEchoClient tcpEchoClient=new TcpEchoClient("127.0.0.1",9090);tcpEchoClient.start();}
}
当开多个线程时,发现只有一个线程在被处理,其它线程都在等待,
当被处理的线程下线后,其他线程的逻辑才开始被处理
原因在于 Socket clientSocket = serverSocket.accept();和processConnection(clientSocket);都是主线程进行处理的且在同一次循环体中,只有一个clinetSocket连接被处理完后,才会去队列中accept下一个连接,为此,这里我们可以采用多线程进行处理。
修改为多线程后,可以看到 有多个客户端可以访问服务器
考虑到一个现实的情况,许多客户端需要频繁的访问服务器,那就是需要频繁的断开/连接,我们这里可以使用线程池
同样也可以实现多个客户端同时访问服务器。
最终的服务器的代码如下:
package network;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;
//基于TCP的回显服务器
public class TcpEachServer {private ServerSocket serverSocket=null;//创建一个非固定数目的线程池private ExecutorService service= Executors.newCachedThreadPool();//绑定端口号public TcpEachServer(int port) throws IOException {serverSocket=new ServerSocket(port);}//启动服务器public void start() throws IOException {System.out.println("服务器开始启动");while(true){//从管理连接的队列中拿出一个“连接”出来Socket clientSocket=serverSocket.accept();//处理这个连接内的请求service.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});
/* Thread t=new Thread(() ->{try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}});t.start();*/}}//这个方法用来处理连接中的逻辑private void processConnection(Socket clientSocket) throws IOException {//日志System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());//下面开始读取请求,计算响应,返回响应 三步曲//Socket对象内部包含两种字节流对象InputStream和OutputStream,可以先把这两个对象流获// 取到,方便后续处理过程种的读写工作try(InputStream inputStream=clientSocket.getInputStream();OutputStream outputStream=clientSocket.getOutputStream()){//不同于UDP协议中的无连接,在客户端的一次连接过程中,可能涉及多次请求/响应过程//因此。这里使用一个while循环,直到该连接中的所有请求处理完毕while(true){//1,读取请求并解析Scanner scanner=new Scanner(inputStream);//hasNext的作用是,检测输入流中是否有结束输入的控制符,比如0x1A(EOF,Ctrl-Z)//用于检测一个连接是否结束if(!scanner.hasNext()){//一个连接处理完毕System.out.printf("[%s:%d] 客户端本次连接处理完毕,下线!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}// 这个代码暗含一个约定, 客户端发过来的请求, 得是文本数据, 同时, 还得带有空白符作为分割. (比如换行这种)//next():当输入到空白符结束String request=scanner.next();//2.根据请求计算响应String response=process(request);//3. 把响应写回客户端,把OutputStream用PrintWriter(此处的PrintWriter相当于Scanner)包裹一下,便于发送数据//将outputStream和PrintWriter关联起来PrintWriter writer=new PrintWriter(outputStream);//使用 PrintWriter 的 println 方法,打印到输出流中 把响应返回给客户端.//此处用 println, 而不是 print 就是为了在结尾加上 \n . 方便客户端读取响应, 使用 scanner.next 读取.writer.println(response);//这里还需要加一个 "刷新缓冲区" 操作.将缓冲区的数据强制输出,用于清空缓冲区writer.flush();//日志 记录当前的请求和响应System.out.printf("[%s:%d] req: %s,resp: %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}} finally {clientSocket.close();}}//回显,只需要再返回这个字符串public String process(String requset){return requset;}public static void main(String[] args) throws IOException {TcpEachServer tcpEachServer=new TcpEachServer(9090);tcpEachServer.start();}
}
上述过程中的一些思路
相关文章:

JavaEE-网络编程套接字(UDP/TCP)
下面写一个简单的UDP客户端服务器流程 思路: 对于服务器端:读取请求,并解析–> 根据解析出的请求,做出响应(这里是一个回显,)–>把响应写回客户端 对于客户端:从控制台读取用户输入的内容–>从控制…...

微服务技术栈-Gateway服务网关
文章目录 前言一、为什么需要网关二、Spring Cloud Gateway三、断言工厂和过滤器1.断言工厂2.过滤器3.全局过滤器4.过滤器执行顺序 四、跨域问题总结 前言 在之前的文章中我们已经介绍了微服务技术中eureka、nacos、ribbon、Feign这几个组件,接下来将介绍另外一个组…...
函数形状有几种定义方式;操作符infer的作用
在 TypeScript 中,函数形状可以用多种方式进行定义。下面介绍了几种常用的函数形状定义方式: 函数声明: function add(a: number, b: number): number {return a b; }在函数声明中,我们直接使用 function 关键字来声明函数&…...

Java / MybatisPlus:JSON处理器的应用,在实体对象中设置对象属性,对象嵌套对象
1、数据库设计 2、定义内部的实体类 /*** Author lgz* Description* Date 2023/9/30.*/ Data // 静态构造staticName,方便构造对象并赋予属性 AllArgsConstructor(staticName "of") NoArgsConstructor ApiModel(value "亲友", description …...

力扣 -- 1027. 最长等差数列
解题步骤: 参考代码: class Solution { public:int longestArithSeqLength(vector<int>& nums) {int nnums.size();int ret2;unordered_map<int,int> hash;//这里可以先把nums[0]存进哈希表中,方便后面i从1开始遍历hash[num…...

正则验证用户名和跨域postmessage
正则验证用户名 字母数字符号大小写8-14匹配用户名的 <!DOCTYPE html> <html> <head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title>form</title> …...

jsbridge实战1:xcode swift 构建iOS app
[[toc]] 环境安装 macOs: 10.15.5 xcode: 11.6 demo:app 创建 hello world iOS app 创建工程步骤 选择:Create a new Xcode project选择:iOS-> single View App填写: project name: swift-app-helloidentifer: smile 包名language: s…...

零基础部署nginx mysql springboot
参考:写给开发人员看的Docker干货,零基础部署nginx mysql springboot 一、连接linux 阿里云 参考:部署到Linux 可能需要购买:购买链接 二、安装docker # 先切换到root用户下 sudo su# 更新apt-get,保证apt-get最新…...
6-3 模式匹配
description 给出主串s和模式串t,其长度均不超过1000。本题要求实现一个函数BF(string s, string t),求出模式串t在主串s中第一次出现的位置(从0开始计算),如果在s中找不到t,则输出-1。 函数接口定义&…...
SQL JOIN 时 USING 和 ON 的异同
在数据表做 join 时,即可以用 using,也可以用 on。有什么异同点呢。 ON 是更加普遍的用法,可以连接表 On 一个字段,多个字段,甚至一个条件表达式。举例 SELECT * FROM world.City JOIN world.Country ON (City.Cou…...
安全学习_开发相关_JNDI介绍(注入)RMILDAP服务
文章目录 参考&本节目的JNDI概念-RMI&LDAP服务调用检索:在RMI服务中调用了InitialContext.lookup()的常用类有:在LDAP服务中调用了InitialContext.lookup()的常用类有: JNDI注入-使用工具生成远程调用JNDI远程调用-工具(j…...

C#学生选课及成绩查询系统
一、项目背景 学生选课及成绩查询系统是一个学校不可缺少的部分,传统的人工管理档案的方式存在着很多的缺点,如:效率低、保密性差等,所以开发一套综合教务系统管理软件很有必要,它应该具有传统的手工管理所无法比拟的…...

【C语言】利用数组处理批量数据(一维数组和二维数组)
前言:在前面学习的程序中使用的变量都属于基本类型,例如整型、字符型、浮点型数据,这些都是简单的数据类型。对于简单的问题,使用这些简单的数据类型就可以了。但是对于有些需要处理的数据,只用以上简单的数据类型是不够的&#x…...

WPF中, 如何将控件的触发事件绑定到ViewModel
在DataGrid 等控件中, 有很多这种带闪电符号的触发事件. 如果用传统的事件驱动, 则直接在后台中建立 一个private PropertyChanged(Sender s, EventAgars Args) 即可. 但是如果需要绑定到ViewModel的话? 应该怎么做? 带闪电符号的触发事件 实现viewModel绑定前端触发事件的…...
解决Qt msvc编译器 中文显示乱码问题
第一步:代码文件选择用utf8编码带bom。第二步:在有中文汉字的代码文件顶部加一行(一般是cpp文件) #pragma execution_character_set(“utf-8”) 可以考虑放在head.h中,然后需要的地方就引入head头文件就行,…...

JAVA面经整理(7)
一)什么是AQS? 1)AQS也被称之为是抽象同步队列,它是JUC包底下的多个组件的底层实现,Lock,CountDownLatch和Semphore底层都使用到了AQS AQS的核心思想就是给予一个等待队列和同步状态来实现的,它的内部使用一个先进先出…...
CentOS7使用技巧
1、防火墙相关 关闭防火墙 systemctl stop firewalld 关闭防火墙开机自启 systemctl disable firewalld.service 查看防火墙状态 systemctl status firewalld...

Nature Machine Intelligence | “化学元素知识+功能提示”双驱动,探索分子预测新方法
论文题目:Knowledge graph-enhanced molecular contrastive learning with functional prompt 论文链接:https://doi.org/10.1038/s42256-023-00654-0 项目地址:GitHub - HICAI-ZJU/KANO: Code and data for the Nature Machine Intelligence…...

CppCheck静态代码检查工具教程【Windows和Linux端】
目录 1、背景 2、特性介绍 2.1、检查结果 2.2、检查范围 2.3、支持的检查规则(列举一些): 2.4、自定义规则 3、linux 端 4、windows 端 1、背景 最近调研了几款 c/c 代码静态检查工具,包括 cppcheck、cpplint、cppdepend、splint、ts…...

W25Q128芯片手册精读
文章目录 前言1. 概述2. 特性3. 封装类型和引脚配置3.1 8焊盘WSON 8x6 mm3.2其他封装 4. 引脚描述4.1 片选4.2 串行数据输入输出4.3 写保护4.4 保持脚4.5 时钟 5. 块图6. 功能描述6.1 SPI功能6.1.1 标准SPI6.1.2 双通道SPI6.1.3 四通道SPI6.1.4 保持功能 6.2 写保护6.2.1 写保护…...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...

PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...

Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...