UDP和TCP网络编程

news2024/11/24 17:54:44

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 {
    //创建一个socket
    DatagramSocket 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","编程");
    }
    @Override
    public 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 {
    //外层socket
    ServerSocket 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 {
    //设置一个全局socket
    private 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.coloradmin.cn/o/662031.html

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

相关文章

2023下半年杭州/广州/深圳软考(中/高级)认证,近期开班

软考是全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;项目&#xff0c;是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试&#xff0c;既属于国家职业资格考试&#xff0c;又是职称资格考试。 系统集成…

一分钟让你轻松拿捏 求解斐波那契数列!

文章目录 斐波那契数列的概念递归求解第N个斐波那契数迭代求解第N个斐波那契数递归法和迭代法的比较 斐波那契数列的概念 斐波那契数列&#xff08;Fibonacci sequence&#xff09;&#xff0c;又称黄金分割数列&#xff0c;因数学家莱昂纳多斐波那契&#xff08;LeonardodaFib…

windows10企业版安装西门子博途V15---01准备环境

网上看到了很多博途安装的文章或视频&#xff0c;一大部分都是你抄抄&#xff0c;我抄抄&#xff0c;滥鱼充饥&#xff0c;一是文章思路不清晰&#xff0c;二是具体安装环境不一致&#xff0c;三是视频讲解混乱&#xff0c;视频不清楚&#xff0c;操作有错误&#xff0c;其中不…

mac外接硬盘在哪里打开 mac外接硬盘用什么格式

Mac电脑具有出色的兼容性和高度的易用性&#xff0c;使得连接外接硬盘变得非常简单。但是&#xff0c;如果你不知道如何打开外接硬盘或者外接硬盘应该使用哪种格式&#xff0c;那么这将成为你使用Mac电脑过程中的一个难题。本篇文章将会向你介绍在Mac上如何打开外接硬盘&#x…

SpringCloud: SpringCloud面试题 ④

前言&#xff1a;面试题是一个以信息整合性看技术特性的一个手段。 1、什么是SpringCloud&#xff1f; springCloud是一系列框架的有序整合。目的在于大规模、分布式、微服务应用部署的解决方案。 2、什么是微服务&#xff1f; 微服务是将一个大而全的业务系统按照一定的业务…

机器学习与深度学习——基于潜在语义分析(LSA)的文档相似度计算

机器学习与深度学习——基于潜在语义分析&#xff08;LSA&#xff09;的文档相似度计算 基于潜在语义分析&#xff08;LSA&#xff09;是一种使用数学和统计方法来分析文本数据的技术。该技术可用于发现文本之间的关系&#xff0c;以及为文本提供有关其含义的更深层次的信息。…

【twcc】学习1:cc-feedback包构造及其接收处理

我的代码是m79,大神的代码看起来比我的新。核心的 ProcessTransportFeedbackInner 竟然没有找到。 webrtc源码分析(8)-拥塞控制(上)-码率预估 再次膜拜一下大神的分析。 本文大量引用大神的论述。 cc-controller下最重要的几个函数来介绍码率控制的核心过程,其分别是OnProce…

perf之cpu性能计数器

在win10主机上的VMware虚拟机里的centos7执行 perf list cache&#xff0c;显示空&#xff1a; [workcontrol-plane ~]$ perf list cache List of pre-defined events (to be used in -e): [workcontrol-plane ~]$ 原因是cpu cache的计数需要硬件支持&#xff0c;在虚拟机里…

使用omp和mpi并行技术实现蒙特卡罗算法实验 (MonteCarlo)

蒙特卡洛算法思想: 画一个正方形和一个内切圆然后在图形上面随机撒点&#xff0c;数一下落在圆 内和正方形内的点的数量之比&#xff0c;就是二者面积之比, 也就是π/4。 大数定理告诉我们&#xff0c;随着样本数量的增大&#xff0c;我们用这种方式模拟出来的值应该是越来越…

玩转电脑|带你了解如何快速查看电脑开关机时间

目录 前言 1、打开管理 2、打开事件查看器 3、打开windows日志 5、获取开机事件 6、获取关机事件 7、保存事件 8、保存事件文件 9、打开事件文件 前言 最近因为一些原因作者想要查看自己电脑每天的的开关机时间记录&#xff0c;但是不知道怎么进行查看&#xff0c;于是在网…

C++重温笔记(十): C++输入输出流

1. 写在前面 c在线编译工具&#xff0c;可快速进行实验: https://www.bejson.com/runcode/cpp920/ 这段时间打算重新把c捡起来&#xff0c; 实习给我的一个体会就是算法工程师是去解决实际问题的&#xff0c;所以呢&#xff0c;不能被算法或者工程局限住&#xff0c;应时刻提…

龙智携手Atlassian亮相DevOps国际峰会:释放团队潜力,以协作挑战不可能

2023年6月29日到30日&#xff0c;龙智将亮相DevOps国际峰会 北京站213展位。本次参展&#xff0c;我们将呈现Atlassian ITSM、DevOps以及工作管理三大解决方案&#xff0c;帮助您释放团队的力量&#xff0c;将不可能变成可能。 立即预约龙智专家面对面交流>>> 龙智自…

BuildAdmin商业级后台管理系统

🚀 CRUD代码生成 图形化拖拽生成后台增删改查代码,自动创建数据表,大气实用的表格、多达22种表单组件支持、拖拽排序、受权限控制的编辑和删除、支持关联表等等,可为您节省大量开发时间。[ 视频介绍 | 使用文档 ] 💥 内置WEB终端 我们内置了WEB终端以实现一些理想中的…

[算法前沿]--019-医学AIGC大模型的构建

基于主动健康的主动性、预防性、精确性、个性化、共建共享、自律性六大特征[1],华南理工大学未来技术学院-广东省数字孪生人重点实验室开源了中文领域生活空间主动健康大模型基座ProactiveHealthGPT,包括:(1) 经过千万规模中文健康对话数据指令微调的生活空间健康大模型扁鹊…

绿色零碳节群硕获奖,为可持续发展提供数字化抓手

6月15日&#xff0c;为探索迈向碳中和之路&#xff0c;第二届国际零碳节暨2023ESG领袖峰会盛大召开。各大绿色品牌与科技集团纷纷现身北京&#xff0c;展示低碳生产与绿色发展的科技成果。群硕也携带创新数字化产品与解决方案亮相国际绿色零碳节&#xff0c;并荣获2023绿色可持…

文献阅读:智能网联环境下面向语义通信的资源分配

目录 智能网联环境下面向语义通信的网络架构模型驱动和数据驱动资源分配算法对比计算卸载和协同计算未来研究方向参考文献 智能网联环境下面向语义通信的网络架构 车辆借助车联网将基于语义理解的计算任务进一步卸载到计算资源丰富的移动边缘计算服务器&#xff08;通常部署在路…

忆联主导消费级存储权威标准制定,推动行业规范化发展

近日&#xff0c;由记忆科技旗下公司忆联主导的团体标准《消费级固态硬盘可靠性及环境适应性规范》&#xff08;以下简称&#xff1a;规范&#xff09;研讨会顺利召开。此次会议完善了《规范》中的相关内容与细节&#xff0c;以确保消费级固态硬盘在可靠性方面能更进一步地满足…

沙利文头豹研究院发布《2023年腾讯云数据安全能力中心分析报告》

2023年6月15日&#xff0c;分析机构沙利文&#xff08;Frost & Sullivan&#xff09;头豹研究院发布《2023年腾讯安全数据安全能力中心分析报告》&#xff08;下文简称&#xff1a;报告&#xff09;&#xff0c;报告针对腾讯安全在数据安全领域的解决思路、产品、安全体系、…

在微信公众平台注册一个小程序开发账号

我们先访问地址 https://mp.weixin.qq.com/ 进入微信公众平台 如果您是第一个进入 直接点上面的 立即注册即可 然后 他会让我选择账号类型 我们要做的是小程序开发 所以 一定要选择小程序的账号类型 下一个界面并没有价值 直接点前往注册即可 注册主要分三个节点 第一步 填…

网络安全学术顶会——CCS '22 议题清单、摘要与总结(中)

注意&#xff1a;本文由GPT4与Claude联合生成。 81、HammerScope: Observing DRAM Power Consumption Using Rowhammer 内存单元尺寸的不断缩小使得内存密度提高&#xff0c;功耗降低&#xff0c;但同时也影响了其可靠性。Rowhammer攻击利用这种降低的可靠性在内存中引发比特翻…