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 写保护…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
