回显服务器的制作方法

文章目录

    • 客户端和服务器
    • TCP和UDP的特点
    • UDP socket api的使用
      • DatagramSocket
      • DatagramPacket
      • InetSocketAddress API
    • 做一个简单的回显服务器
      • UDP版本的回显服务器
      • TCP版本的回显服务器

客户端和服务器

在网络中,主动发起通信的一方是客户端,被动接受的这一方叫做服务器。

  • 客户端给服务器发送的数据是请求(request)
  • 服务器给客户端返回的数据叫响应(response)

客户端和服务器的交互有多种模式

  • 一问一答: 一个请求对应一个响应 。多在网站开发中用到
  • 一问多答:一个请求对应多个相应。主要涉及“下载”的场景中
  • 多问一答:多个请求队对应一个响应 。涉及到“上传”的场景中
  • 多问多答:多个请求对应多个响应。用到“远程控制”和“远程桌面”中

TCP和UDP的特点

要想进行网络编程,需要使用系统的api,本质上是传输层提供的。传输层用到的协议主要是TCP和UDP。这两个协议的差别挺大,所提供的api也有一定的差别。先从本质上看看TCP和UDP的特点有哪些差别。

  • TCP的特点是 有连接,可靠传输,面向字节流,全双工。
  • UDP的特点是 无连接,不可靠传输,面向数据报,全双工。
  1. 有连接/无连接
    举个例子,打电话,需要对方同意后才能打通电话,打电话的过程需要对方确认接听或者不接听,连接的特点就是双方都能认同,而无连接的规则向发微信,自己只管发送,不用在意对方是否同意接收。同理,计算机中的网络连接,就是通信双方,各自保存对方的信息,客户端就有一些数据结构,记录了谁是自己的服务器,服务器也有一些数据结构,记录了谁是自己的客户端。

  2. 可靠传输/不可靠传输
    网络上存在异常情况是非常多的,无论使用什么硬件技术都无法100%保证数据能从A发送到B。此处所说的可靠传输是指尽可能的完成数据传输,即不管数据有没有传输到,A都能清楚的知道。

  3. 面向字节流/面向数据报
    此处提到的字节流和文件中的字节流是一样的。网络中传输数据的基本单位就是字节。
    面向数据报:每次传输的基本单位是一个数据报(有一系列字节构成的)特定的结构。

  4. 全双工/半双工
    一个信道可以双向通信,是全双工。只能单向通信是半双工。

UDP socket api的使用

socket api的中文意思是"网络编程套接字",操作系统中有一类文件叫socket文件,它抽象了网卡这样的硬件设备,而进行网络通信最核心的硬件设备就是网卡,通过网卡发送数据就是写socket文件,通过网卡接收数据就是读socket文件。在UDP中,核心的api有两个类,分别是DatagramSocket和DatagramPacket。下面看看这两个类的使用和注意事项。

DatagramSocket

这个类的作用主要是对soclet文件的读写,也就是借助网卡发送接收数据。接收发送接收数据的单位就是DatagramSocket.。

datagramSocket的构造方法
在这里插入图片描述

DatagramSocket的方法
在这里插入图片描述

DatagramPacket

UDP是面向数据报的,每次发送接收数据的基本单位,就是一个udp数据报,此时表示了一个UDP数据报

DatagramPacket的构造方法
在这里插入图片描述

DatagramPacket方法
在这里插入图片描述

InetSocketAddress API

在这里插入图片描述

做一个简单的回显服务器

UDP版本的回显服务器

回显服务器是客户端发送什么请求,服务器就返回什么响应。做这个服务器的目的是学习UDP socket
api的使用和理解网络编程中客户端和服务器的基本工作流程

服务器的基本逻辑

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;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);socket.receive(requestPacket);// 读到的字节数组, 转成 String 方便后续的逻辑处理.String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2. 根据请求计算响应 (对于 回显服务器来说, 这一步啥都不用做)String response = process(request);// 3. 把响应返回到客户端.//    构造一个 DatagramPacket 作为响应对象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 server = new UdpEchoServer(9090);server.start();}
}
  1. 首先创建DatagramSocket对象,接下来要使用socket对象来操作网卡。在创建对象的时候需要手动指定端口。在网络编程中,服务器一般需要手动指定端口,而客户端一般不需要。一个主机的一个端口只能被一个进程绑定,而一个进程可以绑定多个端口。

  2. 一个服务器一般都是24时运行的,直接使用while(true)可以不用退出。很多时候要重启一个服务器可以直接杀死进程。

  3. 此处 receive就从网卡能读取到一个 UDP 数据报就被放到了 requestPacket 对象中其中 UDP 数据报的载荷部分就被放到 requestPacket 内置的字节数组中了。另外报头部分,也会被 requestPacket 的其他属性保存。除了 UDP 报头之外,还有其他信息,比如收到的数据源 IP 是啥
    通过 requestPacket 还能知道数据从哪里来的(源 ip 源端口) 在这里插入图片描述

  4. 基于字节数组构造出 String字节数组里面保存的内容也不一定就是二进制数据,也可能是文本数据把文本数据交给 String 来保存,恰到好处~~
    这里得到的长度是 requestPacket 中的有效长度,不一定是 40964096 是最大长度。一定是要使用有效长度来构造这里的 String使用最大长度就会生成一个非常长的 String 后半部分都是空白。在这里插入图片描述

  5. 通过process()方法构造响应,这是一个回显服务器,直接返回请求就可以。

  6. 通过requestPacket.getSocketAddress())获得对应客户端的ip和端口。是把请求的源ip和源端口作为响应的目的ip和目的端口。在这里插入图片描述

总结

  • 上述代码中,可以看到,UDP 是无连接的通信 UDP socket 自身不保存对端的IP 和端口.而是在每个数据报中有一个~.另外代码中也没有“建立连接”"接受连接”操作
  • 不可靠传输,代码中体现不到的.
  • 面向数据报,send和receive 都是以 DatagramPacket 为单位
  • 全双工:一个 socket 既可以发送又可以接收

客户端的基本逻辑

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;private String serverIp;private int serverPort;// 此处 ip 使用的字符串, 点分十进制风格. "192.168.2.100"public UdpEchoClient(String serverIp, int serverPort) throws SocketException {//请求的目的IP和目的端口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("-> "); // 表示提示用户接下来要输入内容.// 2. 从控制台读取要发送的请求数据.if (!scanner.hasNext()) {break;}String request = scanner.next();// 3. 构造请求并发送.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);socket.send(requestPacket);// 4. 读取服务器的响应.DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);// 5. 把响应显示到控制台上.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();}
}

三种DatagramSocket对象的构造方法
在这里插入图片描述
网络通信的基本流程

  1. 服务器启动.启动之后,立即进入 while 循,执行到 receive,进入阻塞,此时没有任何客户端发来请求呢。
  2. 客户端启动.启动之后,立即进入 while 循环,执行到 hasNext 这里进入阻塞,此时用户没有在控制台输入任何内容
  3. 用户在客户端的控制台中输入字符串,按下回车此时 hasNext 阻塞解除,next 会返回刚才输入的内容基于用户输入的内容,构造出一个 DatagramPacket 对象,并进行 send 。send执行完毕之后,继续执行到
    reeive 操作,等待服务器返回的响应数据此时服务器还没返回响应呢,这里也会阻塞)
  4. 服务器收到请求之后,就会从 receive 的阻塞中返回返回之后,就会根据读到的 DataqramPacket 对象,构造 String request, 通过 process 方法构造一个 String response再根据 response 构造一个
    DatagramPacket表示响应对象, 再通过 send 来进行发送给客户端。执行这个过程中,客户端也始终在阻塞等待
  5. 客户端从 receive 中返回执行.就能够得到服务器返回的响应并且打印倒控制台上于此同时,服务器进入下一次循环,也要进入到第二次的 receive 阳塞等待下个请求了

TCP版本的回显服务器

tcp中的长短连接
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

  • 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
  • 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

对比以上长短连接,两者区别如下:

  • 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  • 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  • 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于 客户端与服务端通信频繁的场景,如聊天室,实时游戏等服务器的处理逻辑。
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;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();
//            Thread t = new Thread(() -> {
//                processConnection(clientSocket);
//            });
//            t.start();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()) {while (true) {Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {// 读取完毕. 客户端断开连接, 就会产生读取完毕.System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 1. 读取请求并解析. 这里注意隐藏的约定. next 读的时候要读到空白符才会结束.//    因此就要求客户端发来的请求必须带有空白符结尾. 比如 \n 或者空格.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();}
}

在这里插入图片描述
在这里插入图片描述

  • 在服务器代码中,ServerSocket是创建服务端Socket的api。Socket是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket.
    不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。
  • 当客户端发送请求的时候,内核就会发起建立连接的请求,服务器的内核会配合客户端的工作来建立连接,而内核的连接不是决定性的,还需要应用程序把这个连接接受,通过accept方法来接受连接。accept方法方法是会阻塞等待的,当没有客户端发起请求的时候此时就会阻塞。
  • 上面的操作也表现出Tcp是有连接的。

在这里插入图片描述

  • Tcp是面向字节流的。这里的字节流和文件中的字节流完全一致。使用和文件操作一样的类和方法来针对Tcp Socket的读和写。
  • InputStream是往网卡上读数据,OutputStream是往网卡上写数据。

客户端的处理逻辑

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 里面就会保存好这俩信息.// 因此此处 TcpEchoClient 类就不必保存.socket = new Socket(serverIp, serverPort);}public void start() {System.out.println("客户端启动!");try (InputStream inputStream = socket.getInputStream()) {try (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//    这里是和服务器的 scanner.next 呼应的.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();}
}

在TCP回显服务器中,需要注意下面几种情况

  1. 在上面代码中,PrintWriter中内置了缓冲区,IO操作都是很低效的,为了让低效的操作少一些,会引入一个缓冲区,先把写入网卡的数据王道缓冲区中,等达到一定的数量在一次发送出去,但如果发送的数据太少,缓冲区没有满,可能导致数据发送不出去,使用flush方法可以冲刷缓冲区,确保每条消息都能发送出去。

在这里插入图片描述

  1. ServerSocket在整个程序中,只有唯一一个对象,并且这个对象的生命周期伴随着整个程序,这个对象无法提前关闭,只有程序结束,随着进程的销毁一起结束。而clientSocket是每个客户端一个,随着客户端越来越多,如果不释放可能会占满文件描述符表。需要使用close方法关闭。

在这里插入图片描述

  1. 解决多个客户端向一个服务器发送请求的问题

在这里插入图片描述

上面的问题核心思路就是使用多线程,单个线程无法及给客户端提供服务,又能快速调用第二次accept,使用多线程,主线程就负责执行accept,其他线程就负责给客户端提供服务。如果客户端比较多就会频繁的创建销毁线程,就可以使用线程池解决频繁创建销毁线程的问题。

在这里插入图片描述

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

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

相关文章

Rman全备和增量备份说明

RMAN备份分为全备和增量备份,全备不能成为增量备份策略的一部分,它也不能作为后续增量备份的基础。 RMAN增量备份分为0、1、2三级,其中0级备份是增量备份的基础,备份内容也跟全备份一样,要使用增量备份,必…

华为交换机如何配置Web网管登录?直接用网页管理交换机

Web网管是一种对交换机的管理方式,它利用交换机内置的Web服务器,为用户提供图形化的操作界面。用户可以从终端通过HTTPS登录到Web网管,对交换机进行管理和维护,同时也非常方便。 一、配置思路: 1、配置管理IP地址&am…

mysql 自定义函数create function

方便后续查询,做以下记录; 自定义函数是一种与存储过程十分相似的过程式数据库对象, 它与存储过程一样,都是由 SQL 语句和过程式语句组成的代码片段,并且可以被应用程序和其他 SQL 语句调用。 自定义函数与存储过程之间…

提供英语励志名言的软件有哪些?这些软件不可错过

提供英语励志名言的软件有哪些?在追求个人成长和激励的道路上,英语励志名言常常为我们提供前进的动力。这些言简意赅、充满智慧的话语,不仅能在我们迷茫时指引方向,还能在挫折中给予我们勇气和力量。如今,随着科技的发…

stm32和嵌入式linux可以同步学习吗?

在开始前我有一些资料,是我根据网友给的问题精心整理了一份「stm3的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家!!!如果需要使用STM32,建…

http://127.0.0.1:9222/json打不开Chrome环境变量问题

解决方案: 系统环境变量Path设置错误, 1、先看下端口是否占用:netstat -ano|findstr “9222” , 如下127.0.0.1:9222端口显示LISTENING是正常的 如果是SYN_SENT可能不太正常,这个时候, taskkill /PID 端…

【前端素材】推荐优质后台管理系统APP Zina平台模板(附源码)

一、需求分析 当我们从多个层次来详细分析后台管理系统时,可以将其功能和定义进一步细分,以便更好地理解其在不同方面的作用和实际运作。 1. 功能层次 a. 用户管理功能: 用户注册和登录:管理用户账户的注册和登录过程。权限管…

如何使用Docker部署开源Leanote蚂蚁笔记并发布个人博客至公网

最近,我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念,而且内容风趣幽默。我觉得它对大家可能会有所帮助,所以我在此分享。点击这里跳转到网站。 文章目录 1. 安装Docker2. Docker本地部署Leanote蚂蚁笔记3. 安装…

notepad++运行python闪一下就没啦

问题:Notepad直接快捷键运行Python代码,出现闪一下就没了 解决措施: ①点击菜单运行(Run) --> 运行(Run)弹出的对话框 ②把 cmd /k python "$(FULL_CURRENT_PATH)" & ECHO. & PAUSE & EXIT 粘贴进入这个对话框内 ③点击保存&a…

由面试题“Redis是否为单线程”引发的思考

👨‍🎓博主简介 🏅云计算领域优质创作者   🏅华为云开发者社区专家博主   🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入! 🐋 希望大家多多支…

边坡位移监测设备:守护工程安全的前沿科技

随着现代工程建设的飞速发展,边坡位移监测作为预防山体滑坡、泥石流等自然灾害的重要手段,日益受到人们的关注。边坡位移监测设备作为这一领域的关键技术,以其高精度、实时监测的特点,成为守护工程安全的重要武器。 一、边坡位移…

信钰证券|印花税下降对股市的影响?印花税下降利好哪些板块?

投资者在买卖股票时,需求交纳一定的佣金费用、印花税和过户费用,其间印花税和过户费用,随着方针的变动,会进行相应的调整,那么,印花税下降对股市的影响?印花税下降利好哪些板块? 印花…

(科目三)信息技术基础

1.信息与信息技术概述 1.1 信息的定义和表示 一、信息的定义: 信息就是通过语言、文字、图形/图像、声音、视频等传播的内容。 信息≠消息,信息就是消息的内核,消息是信息的外壳。 信息不是某种信号,也不等同于知识。 数据&…

算法沉淀——穷举、暴搜、深搜、回溯、剪枝综合练习三(leetcode真题剖析)

算法沉淀——穷举、暴搜、深搜、回溯、剪枝综合练习三 01.字母大小写全排列02.优美的排列03.N 皇后04.有效的数独 01.字母大小写全排列 题目链接:https://leetcode.cn/problems/letter-case-permutation/ 给定一个字符串 s ,通过将字符串 s 中的每个字…

安装Windows Server 2025 搭建免费云桌面系统

介绍 Windows Server 2025 为 Hyper-V 带来了多项增强功能和新的存储特性,主要用于优化虚拟机的运行体验。这些新特性涵盖 GPU 虚拟化、新的 ReFS 去重功能,以及在非 AD 域的集群上进行虚拟机实时迁移。 云桌面方案的用户最关心的GPU-P的技术也将在Win…

VMware虚拟机文件夹共享失效

问题现象 今天开启虚拟机的时候就看到这个,感觉又要有不好的事情发生了。 果不其然,开机之后弹出这个,我当时还没意识到这个dll文件会对我的正常使用产生什么样的影响。 然后就发现文件根本拷贝不进去虚拟机里面,连虚拟机里面的共…

缓存篇—缓存雪崩、缓存击穿、缓存穿透

缓存异常会面临的三个问题:缓存雪崩、击穿和穿透。 其中,缓存雪崩和缓存击穿主要原因是数据不在缓存中,而导致大量请求访问了数据库,数据库压力骤增,容易引发一系列连锁反应,导致系统奔溃。不过&#xff0…

petalinux_zynq7 驱动DAC以及ADC模块之三:实现C语言API并编译出库被python调用

前文: petalinux_zynq7 C语言驱动DAC以及ADC模块之一:建立IPhttps://blog.csdn.net/qq_27158179/article/details/136234296petalinux_zynq7 C语言驱动DAC以及ADC模块之二:petalinuxhttps://blog.csdn.net/qq_27158179/article/details/1362…

跨界计算与控制,强化显控和UI, 君正MPU再添新旗舰--Ingenic MPU X2600隆重发布

近日,北京君正隆重发布MPU芯片新产品X2600。该产品以商业和工业应用的数个细分领域为重点目标市场,兼顾通用处理器应用需求。无论从CPU结构的设计,还是专门控制器和接口的配备,都体现了北京君正MPU团队“技术路线上追求自主跨界&a…

力扣101 对称二叉树 Java版本

文章目录 题目描述递归解法非递归解法 题目描述 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入:root [1,2,2,3,4,4,3] 输出:true 示例 2: 输入:root [1,2,2,null,3,null,3] 输出…