关于网络编程

目录

1、InetAdress类

2、Socket套接字

3、UDP数据报套接字编程 

(1)DatagramSocket 类

 (2)DatagramPacket类

 (3)处理无连接问题

UdpEchoServer.java

UdpEchoClient.java

4、TCP流套接字编程

(1)工作流程

Server.java

Client.java 

(2)改进

Server.java

Client.java

 运行结果

(3)相关解析

socket.close()

scanner.next()的工作原理

hasNext()工作原理

Ideal同时运行多个进程设置

 服务器同时处理数个客户端请求


指网络上的主机,通过不同的进程以编程的方式实现网络通信(网络数据传输)

1、InetAdress类

Java语言提供了InetAdress类,该类用于处理IP地址主机名称(hostname)的获取

这个类并没有提供构造函数,必须利用该类的静态方法来创建对象

static InetAdressgetByName(String host)
static InetAdress[ ]getAllByName(String host)
static InetAdressgetByAdress(byte[ ] addr)
static InetAdressgetByAdress(String host , byte[ ] addr)
static InetAdressgetLocalHost()返回本地主机的地址
static InetAdressgetLoopbackAdresst()返回回送地址

另外还有些非静态方法用于获取InetAdress对象的信息:

返回值方法名返回数据
byte[ ]getAdress()原始IP地址
String getHostAdress()IP地址字符串
String getHostName()主机名
String getCanonicalHostName()获取此IP地址的完全限定域名

文章链接分享:主机与域名

2、Socket套接字

是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元

基于Socket套接字的网络程序开发就是网络编程

针对传输层协议可以将网络编程分为两类:

(1)流套接字:TCP(传输控制协议)网络通信

        有连接、可靠、面向字节流、有接收/发送缓冲区的、大小不限、全双工

        (适用于需要可靠数据传输的场景,如HTTP、FTP等协议的通信)

(2)数据报套接字:UDP(用户数据报协议)网络通信

        无连接、不可靠、面向数据报、全双工

        (适用于不需要可靠传输,但对实时性有要求的场景,如视频会议、在线游戏等)

有无连接:

通信双方不需要建立连接,可以直接发送数据报

而这里使用Socket进行通信时,双方必须先建立一个连接才能进行数据的发送和接收

面向字节流:即传输数据基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次发送

面向数据报:即传输数据是一块一块的,发送一块数据加入100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节

3、UDP数据报套接字编程 

这里要用到2个类:DatagramSocket类、DatagramPacket类

(1)DatagramSocket 类

        此类表示用于发送和接收数据报数据包的套接字

相关方法:

void receive(DatagramPacket p)从此套接字接收数据报(阻塞直到真正接收到数据报)
void send(DatagramPacket p)从此套接字发送数据报
void close()
 (2)DatagramPacket类

上述DatagramSocket 类中我们发现往这个Socket通道中发送接收都是以一个UDP数据报为单位进行的

但是这个数据报DatagramPacket内部是不能自行分配内存空间,需要手动创建空间,需要通过构造方法往里面放一个字节数组

 (3)处理无连接问题

我们知道UDP是无连接的,意味着服务器与客户端双方并不存储对方的地址与端口号,但是若双方都不知道对方在哪儿,这就传输通信不了了呀!

        银行(服务端)是不需要知道取钱的人(客户端)的地址的,但客户端要知道银行的地址才能找上门请求服务的,所以客户端这边是需要记下指定服务端的地址的。

        但若你还请求了办理证件这种服务,是需要后期邮寄上门的,那么你在请求服务时就会把地址告诉银行。

按照这样的逻辑我们来看下在UDP这里是如何处理无连接问题的

既然UDP自身无法记住,那就由我们另外记录下来

客户端把服务端的地址、端口设置为成员变量,在构造方法中进行赋值,这样就记录了服务端的地址与端口,那么在下面一些列请求中就可以使用了(相当于记下了) 

public UdpEchoClient(String ip,int port) throws SocketException {socket =new DatagramSocket();serverIp=ip;serverPort=port;
}public static void main(String[] args) throws IOException {UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);client.start();
}

而服务端只需要给自己指定端口号即可(给自己定个固定地址,让客户端找上门服务)

 private DatagramSocket socket=null;public UdpEchoServer(int port) throws SocketException {socket=new DatagramSocket(port);}

我们可以发现服务端的端口号是指定的固定的,但是客户端的Socket方法却是让系统自动随机分配一个空闲端口。

原因很简单,试想一下,若同时有数个相同地址相同端口的客户端向同一个服务端发起请求,相同个端口无法区分,那么服务端处理谁呢?所以由系统自动随机分配一个空闲端口可以避免端口冲突!

UdpEchoServer.java

下面我们来写一个简单的回显服务器:(收到什么就返回什么)

public class UdpEchoServer {private DatagramSocket socket=null;public 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);//2、接收并解析数据报socket.receive(requestPacket);String request=new String(requestPacket.getData(),0,requestPacket.getLength());//3、计算响应并返回给客户端String response=process(request);DatagramPacket responsePacket=new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//4、打印日志记录此次数据交互的详情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 server=new UdpEchoServer(9090);server.start();}
}
UdpEchoClient.java
public class UdpEchoClient {private DatagramSocket socket=null;private String serverIp="";private int serverPort=0;public UdpEchoClient(String ip,int port) throws SocketException {socket =new DatagramSocket();serverIp=ip;serverPort=port;}public void start() throws IOException {System.out.println("客户端启动!");Scanner scanner=new Scanner(System.in);while (true){//1、从控制台读取数据作为请求System.out.println("请输入要发送的数据");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);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();}
}

上述只是一个简单的回显服务,若你还想要一个处理复杂点的服务的服务端可以直接继承回显服务端再重写相关方法。比如下面是一个翻译服务:

public class UdpDictServer extends UdpEchoServer{private Map<String,String> dict=new HashMap<>();public UdpDictServer(int port) throws SocketException {super(port);//此处可以往这个表里插入几千几万个这样的英文单词dict.put("dog","小狗");dict.put("cat","小猫");dict.put("pig","小猪");}//重写process方法,在重写的方法中完成翻译的过程(翻译的本质就是”查表“)@Overridepublic String process(String request) {return dict.getOrDefault(request,"该词在词典中不存在!");}public static void main(String[] args) throws IOException {UdpDictServer server=new UdpDictServer(9090);server.start();}
}

4、TCP流套接字编程

用到两个关键的类:

  • SeverSocket类:用于创建服务器绑定端口
  • Socket类:该类实现客户端套接字,套接字是两台机器之间通讯的端点
(1)工作流程

UDP中是各自有一个DatagramSocket类,客户端手动存储服务端的地址与端口,通过数据报发送请求到服务端,服务端就可以接收数据报中的请求、客户端的地址端口,这样就又可以通过数据报把响应返回给客户端,客户端就获得响应,完成网络通信。

这个TCP可就牛杯了。

服务端通过ServerSocket创建服务器绑定端口,等待接收客户端在内核已建立好的连接对象。

客户端Socket s=new Socket(String serverIP,serverPort)就可以与服务端建立连接,服务端这边就能直接获得连接对象Socket对象,就可以直接对Socket对象进行一系列操作(读取请求、写入响应)。

也就是说这下服务端和客户端都是直接对同一个Socket对象进行操作!双方都用这一个Socket对象获取字节输入输出流,这样我都可以直接对同一个socket进行读写了!

    Socket是操作系统中的一个概念,本质上是一种特殊的文件,Socket就属于是把“网卡”这个设备给抽象成文件了。往Socket中写数据,就相当于通过网卡发送数据;从socket文件读数据就相当于通过网卡接收数据。

按照流程来一一梳理下连接完之后双方交互的过程

  1. 客户端 out.write(request)写入请求;
  2. 服务端 in.read()读取请求,服务端 out.write()写入响应;
  3. 客户端 in.read()读取响应

根据这样的流程先简单模拟下:

Server.java
public class Server {public static void main(String[] args) throws IOException {ServerSocket serverSocket=new ServerSocket(9088);System.out.println("服务端上线");while (true){Socket socket = serverSocket.accept();System.out.println("客户端连接成功");try(InputStream in = socket.getInputStream();OutputStream out=socket.getOutputStream()) {byte buf[]=new byte[1024];int n = in.read(buf);String request=new String(buf,0,n);String response="hello";out.write(response.getBytes());}finally {socket.close();System.out.println("客户端下线");}}}
}
Client.java 
public class Client {public static void main(String[] args) throws IOException {Socket socket=new Socket("127.0.0.1",9088);try(InputStream in = socket.getInputStream(); OutputStream out=socket.getOutputStream()){String request="hello";out.write(request.getBytes());byte buf[]=new byte[1024];int n = in.read(buf);String response=new String(buf,0,n);System.out.println (new String(buf,0,n));}}
}

客户端连续连接两次运行结果如下:

(2)改进
Server.java
public class Server {ServerSocket server=null;public Server(int port) throws IOException {server=new ServerSocket(port);System.out.println("服务端上线");}public void start() throws IOException {while (true){Socket socket=server.accept();System.out.printf("[%s:%d] 客户端上线! \n",socket.getInetAddress(),socket.getPort());Thread t=new Thread(()->{try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}});t.start();}}private void processConnection(Socket socket) throws IOException {try(InputStream in=socket.getInputStream();OutputStream out=socket.getOutputStream()){Scanner sc=new Scanner(in);PrintWriter pw=new PrintWriter(out);while (true){if (!sc.hasNext()){//若没有内容则进入阻塞等待输入内容;若有内容则继续循环处理请求//若客户端断开连接(下线),sc就知道不会再有输入内容,就进入if()跳出循环,请求处理完毕,此线程结束System.out.printf("[%s:%d] 客户端下线! \n",socket.getInetAddress(),socket.getPort());break;}String request=sc.next();String response=process(request);pw.println(response);//服务端写入时末尾加了“\n”,作为客户端读取结束标志pw.flush();System.out.printf("[%s,%d] req=%s resp=%s \n",socket.getInetAddress(),socket.getPort(),request,response);}}finally {socket.close();}}private String process(String request){return request;}public static void main(String[] args) throws IOException {Server server=new Server(9789);server.start();}
}
Client.java
public class Client {Socket socket=null;public Client(String serverIp,int serverPort) throws IOException {socket=new Socket(serverIp,serverPort);}public void start() throws IOException {Scanner scanner=new Scanner(System.in);try(InputStream in = socket.getInputStream(); OutputStream out=socket.getOutputStream()){Scanner sc=new Scanner(in);PrintWriter pw=new PrintWriter(out);while (true){System.out.println("请输入请求->");String request= scanner.next();pw.println(request);//客户端写入时末尾加了“\n”,作为服务端读取结束标志pw.flush();String response=sc.next();System.out.printf("返回响应:");System.out.println(response);}}}public static void main(String[] args) throws IOException {Client client=new Client("127.0.0.1",9789);client.start();}
}
 运行结果

(3)相关解析
socket.close()

前面写过的DatagramSocket、ServerSocket都没有close(),因为这两个在整个程序中都只有一个对象,贯穿整个程序,一旦程序结束这两个对象都会自动被销毁,不存在资源泄漏的问题

但是这里服务端接收到的Socket对象则是在循环中每有一个新的客户来建立连接都会创建一个的

并且这个对象最多时用到该客户退出(断开连接)。此时若有很多个客户都来建立连接就意味着会创建很多个Socket对象,当连接断开此时这个对象就会占据着文件描述表的位置。

而在上面的  

try(InputStream in = socket.getInputStream();OutputStream out=socket.getOutputStream())

只是关闭了Socket对象上自带的流对象,而并没有关闭Socket对象本身

scanner.next()的工作原理

用于读取输入,直到遇到空格、Tab键或Enter键等分隔符为止,返回的是内容是连续的有效字符序列,而不包含任何前导或尾随的空白字符

scanner.next()方法提供了一个简单的方式来读取和处理以空白字符分隔的字符串输入 

        比如输入"abc xyz",则会读取并返回"abc",接着若再次调用next()它将返回"xyz"

        在这个过程中,输入的空格被next()视为分隔符,从而确保了每次调用next()时都会返回一段完整的数据

 

hasNext()工作原理

        用来判断缓冲区内是否还有内容,若还有内容,则返回true;若没有内容,不会返回false,而是堵塞当前程序,并且等待输入内容 

Ideal同时运行多个进程设置

 再点击右上角三角形运行图标就可以多个进程运行同一个代码了

  

 服务器同时处理数个客户端请求

上面的代码我们给出的解决方案是每来一个客户端就新创建一个线程处理请求,这样的解决办法在数个客户端请求的情况下,会频繁地来进行建立、断开连接,会导致服务器频繁地创建、销毁线程,这样的开销是巨大的!

为了进一步优化,我们可以使用线程池、协程、或者I/O多路复用等手段。

由于作者只会线程池,所以这里就写下线程池:

 public void start() throws IOException {ExecutorService service= Executors.newCachedThreadPool();while (true){Socket socket=server.accept();System.out.printf("[%s:%d] 客户端上线! \n",socket.getInetAddress(),socket.getPort());service.submit(new Runnable() {@Overridepublic void run() {try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}}});}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/340074.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习之数学基础(六)~时间复杂度和空间复杂度

目录 算法背景 background 1. 时间复杂度 Time Complexity 1.1 时间复杂度分类 1.1.1 O(1) 常数阶 1.1.2 O(n) 线性阶 1.1.3 O(n^2) 平方阶 1.1.4 O(logn) 对数阶 1.1.5 O(nlogn) 线性对数阶 1.1.6 O(2^n) 指数阶 1.1.7 O(n!) 阶乘阶 1.1.8 时间复杂度分类 1.2 时…

基于FPGA的SystemVerilog练习

文章目录 一、认识SystemVerilogSystemVerilog的语言特性SystemVerilog的应用领域SystemVerilog的优势SystemVerilog的未来发展方向 二、流水灯代码流水灯部分testbench仿真文件 三、用systemVerilog实现超声波测距计时器测距部分led部分数码管部分采样部分顶层文件引脚绑定效果…

华为昇腾310B初体验,OrangePi AIpro开发板使用测评

0、写在前面 很高兴收到官方的OrangePi AIpro开发板测试邀请&#xff0c;在过去的几年中&#xff0c;我在自己的博客写了一系列有关搭载嵌入式Linux系统的SBC&#xff08;单板计算机&#xff09;的博文&#xff0c;包括树莓派4系列、2K1000龙芯教育派、Radxa Rock5B、BeagleBo…

001----flask

flask---001 flask与django对比今日概要问答今日详细1.flask快速使用1.2 快速使用flask1.3 用户名密码登录 flask与django对比 django是个大而全的框架&#xff0c;flask是一个轻量级的框架。 django内部为我们提供了非常多的组件&#xff1a;orm/session/cookie/admin/from/mo…

mysql 分区

目标 给一个表&#xff08;半年有800万&#xff09;增加分区以增加查询速度 约束 分区不能有外键否则会报错 https://blog.csdn.net/yabingshi_tech/article/details/52241034 主键 按照时间列进行分区 https://blog.csdn.net/winerpro/article/details/135736454 参看以…

时序预测 | Matlab灰色-马尔科夫预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab灰色-马尔科夫预测 灰色马尔科夫预测&#xff08;Grey-Markov Prediction&#xff09;是一种用于时间序列预测的方法&#xff0c;它结合了灰色系统理论和马尔科夫链模型。灰色系统理论是一种非参数化的预测方法…

[vue2项目]vue2+supermap[mapboxgl]+天地图之地图的初始化

Supermap参考教程 天地图 一、安装 1、终端:npm install supermap/vue-iclient-mapboxgl 2、在package.json文件的dependencies查看supermap/vue-iclient-mapboxgl依赖是否安装成功。 3、在mian.js全局引入 import VueiClient from supermap/vue-iclient-mapboxgl; Vue.use(…

研学活动报名收集材料怎么写?教程来了!

研学活动作为学校教育的重要组成部分&#xff0c;不仅能够拓宽学生的视野&#xff0c;还能促进家校沟通。学生们报名还是十分积极踊跃的&#xff0c;然而研学活动报名收集材料该怎么写却困扰着不少老师&#xff0c;其实只需要把姓名和联系方式等收集全就可以了&#xff0c;主要…

typescript --object对象类型

ts中的object const obj new Object()Object 这里的Object是Object类型&#xff0c;而不是JavaScript内置的Object构造函数。 这里的Object是一种类型&#xff0c;而Object()构造函数表示一个值。 Object()构造函数的ts代码 interface ObjectConstructor{readonly prototyp…

UMG绝对坐标与局部空间

在 Unreal Engine 的 UMG&#xff08;Unreal Motion Graphics&#xff09;中&#xff0c;“绝对坐标”和“局部空间”是两个常见的概念&#xff0c;主要用于描述 UI 元素的位置和大小。 概念与区别 绝对坐标&#xff08;Absolute Coordinates&#xff09;&#xff1a;这是指相…

[数据集][目标检测]RSNA肺炎检测数据集VOC+YOLO格式6012张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6012 标注数量(xml文件个数)&#xff1a;6012 标注数量(txt文件个数)&#xff1a;6012 标注…

彩光大放异彩!《智慧园区以太全光网络建设技术规程》应用案例征集活动结果公布

近日,中国建筑业协会绿色建造与智能建筑分会正式公布了《智慧园区以太全光网络建设技术规程》应用案例征集活动的结果。本次活动旨在推广和应用该规程,进一步推动智慧园区的数字化、智慧化、绿色化建设。众多优秀项目在征集活动中脱颖而出,展示了规程在实际应用中的显著成效。评…

如果任务过多,队列积压怎么处理?

如果任务过多,队列积压怎么处理? 1、内存队列满了应该怎么办2、问题要治本——发短信导致吞吐量降低的问题不能忽略!!3、多路复用IO模型的核心组件简介1、内存队列满了应该怎么办 如图: 大家可以看到,虽然现在发短信和广告投递,彼此之间的执行效率不受彼此影响,但是请…

vue3+typescript 使用Codemirror

安装 // npm npm install codemirror-editor-vue3 codemirror^5.65.12// ts版 还需安装&#xff1a; npm install types/codemirror全局注册 修改main.ts&#xff1a; import { createApp } from vueimport App from ./App.vueimport { InstallCodemirro } from "code…

M-G364PD惯性测量单元:相机及微小层面的革命性应用

在现代科技飞速发展的今天&#xff0c;精准控制和精确测量是众多高端设备实现卓越性能的关键。爱普生推出的M-G364PD惯性测量单元&#xff08;IMU&#xff09;&#xff0c;因其卓越的性能和微小尺寸&#xff0c;成为相机以及其他微小层面应用的理想选择&#xff0c;为科技创新提…

JVMの堆、栈内存存储

1、JVM栈的数据存储 通过前面的学习&#xff0c;我们知道&#xff0c;将源代码编译成字节码文件后&#xff0c;JVM会对其中的字节码指令解释执行&#xff0c;在解释执行的过程中&#xff0c;又利用到了栈区的操作数栈和局部变量表两部分。 而局部变量表又分为一个个的槽位&…

JavaScript 基础 - 对象

对象 对象是一种无序的数据集合&#xff0c;可以详细的描述描述某个事物。 注意数组是有序的数据集合。它由属性和方法两部分构成。 语法 声明一个对象类型的变量与之前声明一个数值或字符串类型的变量没有本质上的区别。 <script>let 对象名 {属性名&#xff1a;属性值…

DynamiCrafter ComfyUI 教程 | 对图片转视频的效果进行精细化控制

近日&#xff0c;由北大、腾讯AI Lab联合推出的 AI 视频生成工具 DynamiCrafter 一经上线便引起了巨大反响。只需要输入一张普普通通的静态图&#xff0c;加上几句简单的文字引导&#xff0c;瞬间就能生成超逼真的动态视频&#xff0c;简直不要太厉害&#xff01; 静态图 fire…

renren-fast-vue启动报错

问题描述 拉取人人开源vue项目启动失败 报错信息 版本信息 序号名称版本号1node14.21.3 启动方案 1.拉取项目 git clone https://gitee.com/renrenio/renren-fast-vue.git 2.执行安装依赖命令 npm install 3.此时报错 chromedriver2.27.2 install: node install.js 4.手动…

kafka集群内外网分流方案——筑梦之路

前言 在现代分布式系统架构中&#xff0c;Kafka作为一款高性能的消息队列系统&#xff0c;广泛应用于大数据处理、实时流处理以及微服务间的异步通信场景。特别是往往企业级应用中&#xff0c;业务网段和内网通信网段不是同一个网段&#xff0c;内网的机器想要访问业务数据只能…