UDP和TCP网络编程

UDP和TCP网络编程

    • UDP网络编程
      • UDP通信流程(回显服务)
      • 测试
      • 扩展《UDP字典查找单词》
    • TCP网络编程
      • TCP互相通信
      • 测试
    • 缓存区和缓存

UDP网络编程

特点:

  • 无连接:发送数据前不需要建立连接。
  • 不可靠:没有重发机制,无法保证数据的可靠性和顺序。
  • 无拥塞控制:发送方发送数据的速度受限于网络的带宽。
  • 快速:由于没有连接建立和拥塞控制的处理,UDP的传输速度通常比TCP快。
  • 简单:UDP协议的头部较小,仅有8个字节的固定长度头部。

UDP通信流程(回显服务)

实现服务层步骤

  1. 创建一个socket用于通信DatagramSocket

  2. 等待浏览器分配端口port

  3. 使用socket读取用户的请求

    • 启动服务器后如果没有客户端发来请求,则会一直阻塞等待

    • 首先需要创建一个容器packets设置容器大小以字节数组为单位

    • 然后使用socket接收socket.receive(packet);

    • 接收以后,此时的packet中就保存了用户发来的请求

    • 为了方便处理,使用字符串来接收该请求取名为request

  4. 根据请求计算响应数据,因为我们的服务是回显服务,所以只需要返回请求即可response = request

  5. 把响应结果返回给客户端,和刚刚封装请求一样,这里也同样使用一个DatagermPacket 来构造响应数据。

    • 这里的响应取名为responsePacket,需要将客户端的Ip、端口一并与响应数据进行封装
    • 最后使用socket发送响应数据包socket.send(responsePacket);
  6. 打印数据(非必须)

服务器具体代码实现

public class DirServer {//创建一个socketDatagramSocket socket =null;public DirServer(int port) throws SocketException {socket = new DatagramSocket(port);}//启动服务器总逻辑public void start() throws IOException {System.out.println("服务器启动!");while (true){//每次循环//1. 读取请求并响应DatagramPacket packet = new DatagramPacket(new byte[1024],1024);//从socket中获取数据存储在packet中socket.receive(packet);//拿着packet中的字节进行构造String request = new String(packet.getData(),0,packet.getLength());//2. 根据请求计算响应String response = process(request);//3. 把响应结果返回给客户端// 和请求packet不同,这里构造响应时需要指定目的地,也就是发送请求的客户端的地址DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,packet.getSocketAddress());socket.send(responsePacket);//打印请求数据和响应数据System.out.printf("[%s:%s]req : %s,resp:%s\n",packet.getAddress(),packet.getPort(),request,response);}}//构造响应数据public String process(String request) {return request;}//主函数public static void main(String[] args) throws IOException {DirServer dirServer = new DirServer(9090);dirServer.start();}

实现客户端步骤

  1. 与服务端相同,这里需要指定一个socket来发送请求,socket需要设置服务器的IP和端口

  2. 输入请求,这里是从控制台获得请求数据使用输入方法Scanner

    • 同样的将这个字符串构造成一个字符串
  3. 把从控制台读取的字符串进行打包,使用DatagramPacket

    • 将请求数据与端口信息和IP地址一同打包在一个requestPacket
    • 发送请求使用socket.send(requestPacket)
    • 等待服务器的响应
  4. 接收响应数据,也需要使用一个DatagramPacket设置容器的大小用于接收响应socket.receive(responsePacket);

  5. 将响应数据转化为字符串进行显示response

  6. 打印请求数据与响应数据

客户端具体代码实现

public class DirClient {//设置一个全局变量private DatagramSocket socket = null;private String serverIp;//IP地址private int port;//端口号//构造方法,将IP和端口号传入socket中,连接服务器public DirClient(String serverIP,int serverPort) throws SocketException {//对于客户端来说,不需要显示关联窗口//不过也需要端口,系统会根据需求自动分配一个端口socket = new DatagramSocket();this.serverIp = serverIP;this.port = serverPort;}//启动方法public void start() throws IOException {Scanner scanner = new Scanner(System.in);while (true){//1. 先从控制台读取一个字符串//   打印一个提示符,提示用户需要输入的内容System.out.print("--》");String request = scanner.next();//2. 把字符串构成UDP packet进行发送// 通过packet打包,需要发送的ip地址及端口号分别设置值,DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp), port);socket.send(requestPacket);//3. 客户端尝试收取服务器返回的响应DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024);socket.receive(responsePacket);//4.把响应数据转换为String进行显示String response = new String(responsePacket.getData(),0,requestPacket.getLength());System.out.printf("req: %s,resp : %s \n",request,response);}}//主函数public static void main(String[] args) throws IOException {DirClient udpClient = new DirClient("127.0.0.1",9090);udpClient.start();}
}

测试

🕵️‍♂️先启动服务器,再打开客户端,可以设置一个输入exit就退出的逻辑。

在输入时判断输入的是否为exit

Scanner scanner = new Scanner(System.in);
String request = scanner.next();
if(request.equals("exit"))break;

服务器打印

在这里插入图片描述

客户端打印

在这里插入图片描述

扩展《UDP字典查找单词》

单纯使用一个UDP字典进行回显未免有点无聊😩

反正UDP是进行回显,不如回显一些更有意思的事情,比如困恼我们许久的单词!

  • 设计思路
    1. 使用一个map来保存单词和翻译
    2. 读取客户端的单词
    3. 在map中查找单词的翻译返回响应

我们基于刚刚实现的回显程序,只需要改动一下响应数据即可。

public class AddDir extends DirServer{HashMap<String,String> dictionary = new HashMap<>();//需要在构造时把单词和翻译写入map//读者可根据需要自行修改public AddDir(int port) throws SocketException {super(port);dictionary.put("programmer","程序员");dictionary.put("great","厉害");dictionary.put("programming","编程");}@Overridepublic String process(String request){return dictionary.getOrDefault(request,"没有这个单词");}public static void main(String[] args) throws IOException {AddDir addDir = new AddDir(9090);addDir.start();}
}

测试:

客户端(出现乱码,是因为使用的字符集不匹配,可以在响应数据中设置编码格式解决)

在这里插入图片描述

服务器:

在这里插入图片描述

TCP网络编程

TCP的特点

  • 面向连接:发送数据前必须先与对方建立连接。
  • 可靠:具有数据的完整性保护和包的顺序控制,能够确保数据的可靠性和正确性。
  • 有拥塞控制:TCP会通过动态调整发送数据的速度防止网络拥塞
  • 慢速:连接的建立需要时间,有较长的等待时间和握手过程,传输速度相对缓慢。(三次握手)
  • 复杂:TCP协议的头部较大,有20个字节的固定长度

TCP互相通信

实现服务器步骤:

  1. TCP与UDP不同,需要连接,启动服务器需要传递IP。TCP需要使用另外一个Socket来管理一个用户,所以第一步需要创建两个不同的socket服务器。

    • 外层ServerSocket主要负责对业务处理
    • 内层socket是将请求统一打包给服务器内部
    • 实际ServerSocket只有一个,其他的都是由这个accept分离出去的socket
  2. 通过接收请求的方法accept获取新socket

  3. 创建一对输入输出字符流,使用输入流去读取请求信息,使用输出流返回响应到客户端

    • inputStream输入流接收请求
    • outputStream输出流返回响应
  4. 读取请求数据,请求和响应都可能有多个

  5. 根据请求数据计算响应数据,为了更简单的包装请求,不需要一个一个字节去计算,所以这里设置为字符串类型

  6. 返回响应数据到客户端PrintWriter.println(响应数据)

  7. 打印信息

服务端具体代码实现

public class TCPEchoServer {//外层socketServerSocket socket = null;//构造方法public  TCPEchoServer(int port) throws IOException {socket = new ServerSocket(port);}//启动方法public void start() throws IOException {System.out.println("服务器启动");while (true){//接收到一个请求Socket accept = socket.accept();processConnection(accept);}}//通过这个方法处理一个连接public void processConnection(Socket clientSocket) throws IOException {System.out.println("客户端上线!"+clientSocket.getLocalSocketAddress().toString());//创建一个输入流和输出流//在这里需要简单约定一下,每个请求使用\n来分割try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){//创建一个字符流,接收响应数据//Scanner scanner = new Scanner(inputStream);//相当于字符流PrintWriter writer = new PrintWriter(outputStream,true);while (true){//1. 读取请求if(!scanner.hasNext()){//读取数据结束,(对端关闭)System.out.println("客户端下线!"+clientSocket.getInetAddress().toString());break;}String request = scanner.next();//2. 根据请求计算响应(调用响应方法)String response = process(request);//3. 把响应返回给客户端(一个或多个请求或响应)writer.println(response);System.out.println("address:"+clientSocket.getInetAddress()+"port: "+clientSocket.getPort()+" req:"+request+" reps: "+response);}}catch (IOException e){e.printStackTrace();}finally {clientSocket.close();}}//返回响应数据private String process(String request) {Scanner scanner = new Scanner(System.in);System.out.println("请求:"+ request+" 你的回复:");String response = scanner.next();return response;}//主函数public static void main(String[] args) throws IOException {TCPEchoServer tcpEchoServer = new TCPEchoServer(9090);tcpEchoServer.start();}
}

实现客户端步骤:

  1. TCP可以连接多个客户端进行通信,客户端连接服务器需要创建一个socket
  2. 使用InputStream在控制台接收用户输入的数据
  3. 使用PrintWriter进行封装数据作为客户端请求request
  4. 发送客户端请求printWriter.println(request);
  5. 从服务器接收响应数据。使用一个字符串用于接收响应response
  6. 打印响应数据

具体代码实现:

public class TCPEchoClient {//设置一个全局socketprivate Socket socket = null;public TCPEchoClient(int port,String serverIp) throws IOException {socket = new Socket(serverIp,port);}public void start(){Scanner scanner = new Scanner(System.in);try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){Scanner scannerFromSocket = new Scanner(inputStream);//注意:这里有一个ture意味着刷新缓存区PrintWriter printWriter = new PrintWriter(outputStream,true);while (true){//1.从键盘读取请求(用户输入的内容)System.out.println("-> ");String request = scanner.next();//2.将读取的请求,发送给服务器//无换行内容printWriter.println(request);//3.从服务器读取响应内容String response = scannerFromSocket.next();//4.把响应数据结果显示到控制台System.out.println("req: "+request+"resp:"+response);}}catch (IOException e){e.printStackTrace();}finally {}}//主函数public static void main(String[] args) throws IOException {TCPEchoClient tcpEchoClient = new TCPEchoClient(9090,"127.0.0.1");tcpEchoClient.start();}
}

测试

服务器

在这里插入图片描述

客户端

在这里插入图片描述

缓存区和缓存

在TCP通信中,如果没有加fulsh刷新缓存区就无法即使读取请求数据,也无法及时收到响应数据,那么这是什么原因导致的呢?

其实,在内存中存在缓存区,缓存区是相当于池子一样的,可以用于存放数据的容器。 而与我们经常提到的缓存是两个不一样的东西,缓存是一种计算机系统的技术。

缓存:

用于提高数据读取和写入效率。当计算机需要执行或者读取某个文件时,会将一部分数据加载到缓存中,下一次需要的时候,直接从缓存中获取即可,避免了不必要的磁盘读写操作,提高访问速度。

缓存区:

计算机分配的一块内存空间,用来存储数据,通常是临时存储,在进行IO操作时,当数据读取到了缓存区,可以由程序直接操作,提高了数据读取效率

TCP中printWrite首先是在硬盘中写入数据(请求),为了提高效率,数据被写入缓存区,缓存区有一个特性就是满了才会将数据进行统一提交(提交到网卡),这样才能读到请求。显然,这是不现实的,所以这里我们需要进行刷新(Flush)操作。

可使用两种方法进行刷新:

自动刷新(构造PrintWrite时设置):

PrintWriter printWriter = new PrintWriter(outputStream/*输出流*/,true);

手动刷新(需要设置在客户端发送请求数据后):

printWriter.flush();

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

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

相关文章

面对Android开发高薪诱惑,如何拿到大厂offer?

前言 出来打工最看重的就是薪资&#xff0c;作为一名5年开发经验的Android人员。在如今的大环境中薪资一降再降&#xff0c;还没人要。技术还还算可以但是面试一直被刷下来&#xff1f;这是为什么&#xff0c;本篇我们来聊聊Android开发面试中的关键。 面试过程 通常面试过程…

VRRP协议是什么?详解它的工作原理

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 前言 本文将讲解VRRP是什么&#xff0c;以及它的工作原理&#xff0c;优点以及基本概念。 目录 一、VRRP协议是什么&#xff1f; 二、VRR…

【FPGA】Verilog:时序电路设计 | 自循环移位寄存器 | 环形计数 | 扭环计数 | 约翰逊计数器

前言&#xff1a;本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例&#xff1a;计数器 ​​ 功能特性&#xff1a; 采用 Xilinx Artix-7 XC7A35T芯片 配置方式&#xff1a;USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度 存储器&#xff1a;2Mb…

将当前conda环境导出为yaml文件

conda环境的转移和复制 conda导出已有环境&#xff0c;环境会被保存在environment.yaml文件中。 conda env export > environment.yaml 当我们想再次创建该环境&#xff0c;或根据别人提供的.yaml文件复现环境时&#xff0c;就可以通过下面的命令来复现安装环境了。 conda …

【Docker】子系统与其相关名词的界定、Control Groups等详细讲解

前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。 &#x1f4d5;作者简介&#xff1a;热…

tcp、udp调试工具

NetAssist(网络调试助手) 下载地址&#xff1a;http://www.cmsoft.cn/resource/102.html tcp-server代码 # codingutf-8 # 多线程TCP服务器import socket import threadingbind_ip "192.168.137.1" # 监听的IP 地址 bind_port 5100 # 监听的端口# 建立一个socke…

用OpenCV进行OCR字符分割

1. 引言 本文重点介绍如何利用传统的图像处理的方法来进行OCR字符切分&#xff0c;进而可以用分割后的单个字符做相应的后续任务&#xff0c;虽然现在计算机视觉依然是卷积神经网络的天下&#xff0c;但是对于一些相对简单的落地场景传统方案还是很有效的。 闲话少说&#xff…

c#学习笔记

目录 1.语句块2.Write和WriteLine的区别&#xff1a;3.params4.托管代码和非托管代码5.DllImport的使用&#xff1a;6.WriteLine、ReadLine和ReadKey&#xff1a;7.C#中访问修饰符8.类型的实例化9.成员可以分为两种&#xff1a;数据成员和函数成员10.枚举enum和结构struct的区别…

Cesium 实战 - 使用 gltf-vscode 查看、预览以及编辑 glTF 和 GLB 模型

Cesium 实战 - 使用 gltf-vscode 查看、预览以及编辑 glTF 和 GLB 模型 VScode&#xff08;Visual Studio Code&#xff09; 安装模型必要插件VScode 预览自定义关节&#xff08;articulations&#xff09;动作VScode 导入 GLB 格式模型VScode 导出 GLB 格式模型Cesium 在线示例…

内网穿透技术

文章目录 前言1. 安装JAVA2. MCSManager安装3.局域网访问MCSM4.创建我的世界服务器5.局域网联机测试6.安装cpolar内网穿透7. 配置公网访问地址8.远程联机测试9. 配置固定远程联机端口地址9.1 保留一个固定tcp地址9.2 配置固定公网TCP地址9.3 使用固定公网地址远程联机 转载自内…

SpringBoot3.0整合RocketMQ时出现未能加载bean文件

SpringBoot3.0整合RocketMQ时出现未能加载bean文件 问题 APPLICATION FAILED TO START Description: Field rocketMQTemplate in com.spt.message.service.MqProducerService required a bean of type ‘org.apache.rocketmq.spring.core.RocketMQTemplate’ that could not …

STM32F4 点亮灯泡【顺序点亮、按键点亮】

一、顺序点亮灯泡 ①初始化 在user.c文件中&#xff0c;我们需要对LED进行初始化设置。 在函数LED_GPIO_Config中&#xff0c;可以修改代码如下&#xff1a; /*********************************************************************** LED初始化 备注 LED 接在GPC14引脚上…