JavaEE - 网络编程之回显服务器

news2025/1/23 1:11:36

目录

一.什么是回显服务器?

二.UDP是什么?

1.TCP 是有链接的, UDP 是无连接的

2.TCP是可靠传输的,UDP是不可靠传输的

3.TCP是面向字节流的,UDP是面向数据报

4.TCP和UDP是全双工的

三.UDP的 socket api 

四. 具体代码实现

1.服务器部分

2.客户端部分 

 3. 具体的流程到底是个啥?

4.执行结果


一.什么是回显服务器?

回显服务器是网络编程中一个简单的代码示例,回显的意思就是客户端发给服务器什么东西,服务器就返回给客户端什么东西。此处我们使用UDP来进行编写回显服务器。

既然此处我们要使用UDP来编写回显服务器,那么我们就有必要去了解UDP是什么?

二.UDP是什么?

UDP是五层网络模型中传输层的协议。其实这个传输层的协议有两个,一个是TCP,另一个就是UDP。两者的区别如下:

1.TCP 是有链接的, UDP 是无连接的

此处链接的本质就是建立连接的双方,各自保存对方的信息.

TCP要想通信,就需要先建立连接(保存对方信息),然后才能后续通信。

如果A 想和 B 建立连接,但是 B 拒绝了!通信就无法完成!

UDP要想建立链接,就直接发送数据即可,不需要征得对方的同意,UDP自身也不会保存对方信息。但是应用程序层面会知道。

2.TCP是可靠传输的,UDP是不可靠传输的

什么是可靠?究竟什么样子才算可靠?

其实这个可靠是一个模糊的概念,比如我是一个非常厉害的老中医,但是假如有病人问我这个病能不能百分百治好的时候,我只能说:“我尽力治好~~~” 。  此时我是可靠的呢还是不可靠的呢?其实应该是可靠的,因为此时我的医术很精明,已经很接近有百分百的把握了。

在网络上进行通信,A - >B 的过程中,这个消息是不可能 100% 送达的

TCP内置了可靠传输机制,发送失败的时候会采取一定的措施(比如尝试重传之类的)

UDP就没有内置可靠传输机制!

但是我们思考,为什么UDP不搞一个可靠传输呢?

因为可靠传输是要付出代价的: 机制更复杂    传输效率更低~

3.TCP是面向字节流的,UDP是面向数据报

TCP是以字节为单位来进行传输的.

UDP是以数据报为单位来进行传输的.

4.TCP和UDP是全双工的

也就是两者都允许双向通信,客户端可以发送请求给服务器,服务器也可以发送相应给客户端。

三.UDP的 socket api 

socket本质上就是一个特殊的文件,把“网卡”抽象成了文件

往socket 文件中写数据,就相当于通过网卡发送数据

往socket 文件中读数据,就相当于通过网卡接收数据

在Java中,UDP的API主要有两种:

DatagramSocket  和  DatagramPacket

其实这两个对象,可以这样理解:DatagramSocket  就相当于一个网关文件, 而 DatagramPacket就相当于穿梭在这个网关文件中的数据报。

也就是客户端要发送这个数据报给服务器 ,  服务器要把相应再以数据报的形式发给客户端。

四. 具体代码实现

1.服务器部分

package net;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
    //创建一个DatagramSocket对象 这个就相当于一个网卡文件
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        //这样写就是手动指定端口
        socket = new DatagramSocket(port);

        //socket = new DatagramSocket();  这样写就是系统自动分配端口
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            //1. 读取请求并解析
            //这里首先要创建出数据报类型的对象
            //此处的requestPacket对象就是作为输出型参数进行传递
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            //在receive之后,requestPacket中已经存储好了二进制数据

            //但是要想显示出来,就需要把这个二进制转为字符串
            //此处意思就是从 0 到数据的最大长度
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());

            //2.根据请求计算相应
            //由于是回显服务器,请求是啥样,相应就是啥样
            String response = process(request);

            //3.把响应写回到客户端中
            //此时搞一个响应对象,DatagramPacket,在这个里面构造刚才的数据,再返回
            //注意response.getBytes().length 和 response.length 是不一样的,
            // 一个是获取字节的长度,另一个是获取字符的长度。如果response全是英文字符串,那就没事。但是如有中文的话就可能会出现问题
            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);
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}

代码解读:

1. 服务器要手动指定端口, socket = new DatagramSocket(port); 这样的目的就是让多个客户端能够精准访问。

2. process 方法就是一个简单的 传进去什么,返回什么东西。这个就是回显的体现。

3.DatagramSocket 有不同的版本 , 我们要根据需要 使用不同的参数.

        比如在服务器进行接收的时候,就需要先构造好一个空的DatagramSocket对象(相当于空盘子),然后放到recevie里面等待客户端发来东西。然后把客户端的东西放到这个对象中(放到盘子里)。

        比如在服务器进行回应的时候,此时的DatagramSocket里面就应该是response ,也就是根据这个response 字符串进行构造DatagramSocket对象。

4.requestPacket.getSocketAddress() 意思就是服务器回应的时候,需要知道是谁发来的。

2.客户端部分 

package net;

import Inner.Inter;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

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.print("-> ");
            String request = scanner.next();

            //2.将请求内容构造成DatagramPacket数据报对象
            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);

            //4.把相应拼接成字符串并显示出来
            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("172.0.0.1",9090);
        client.start();
    }
}


代码解读:

1. 客户端需要使用系统自动分配的端口。这是为什么呢?我们来想一下,加入我在我们大学开了一个窗口,那么我就是一个服务器,也就是给大家做饭的。各位同学们就是客户端。那么我的窗口位置就需要确定,以让同学们更好的找到我(也就是服务器的端口要手动指定)。那么各位同学在等待我做好饭的时候(等待服务器回应),所等待的位置是不是每次都相同?答案很明显:不是。 同学们等待我做饭的位置,是不定的!!!也就是可以理解为客户端的端口是随机的,是系统自动分配的

2. 知道需要分配端口号之后,客户端这边需要发送一个请求给服务器。那么这个请求需要构造成DatagramSocket 对象类型,才能够进行传输。

3.InetAddress.getByName(serverIp) 这个就是将服务IP 转化为数据报的形式,因为我们自己写的IP是字符串。

4. 客户端通过sent发送请求,然后receive等待相应。注意等待相应之前,应该先有一个空的DatagramSocket对象(空盘子)来接收这个相应!

 3. 具体的流程到底是个啥?

1). 首先服务器先启动。服务器启动之后,就会进入循环,执行到 receive 这里进行阻塞(因为客户端还没有发送请求过来);

2). 客户端也开始启动, 先进入while 循环 执行 scanner.next 并且也在这里阻塞. 直到用户输入一个内容

3). 客户端发送数据之后(服务器和客户端会同步执行)

     服务器: 就会从receive 中返回, 进一步的解析请求为字符串 , 指定 process方法, 执行sent 操作,执行打印操作.

     客户端:  继续往下执行,指定到客户端的 receive阻塞, 等待服务器的响应

4). 客户端收到从服务器返回的数据之后 , 就会从 receive 中停止阻塞并返回 . 然后执行打印操作

5). 服务器这边完成一次循环之后 , 又执行到了 receive这里

    客户端这边完成一次循环之后, 执行到了 scanner.next 这里

        双双进入阻塞, 如此再循环往复~~~~~

4.执行结果


总结: 写UDP版本的环回服务器 + 客户端的过程可以加深我们对于网络编程的细节概念。到这里我们就突破了次元壁,大家可以尝试在局域网内互相发送消息。只需要将服务器文件发送给另一台电脑并且执行,客户端这边改一下 IP 就可以了。大家可以尝试一下!

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

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

相关文章

PPT录制视频的方法,轻松提升演示效果!

在现代工作和学习中,ppt是一种常见的演示工具,而将ppt转化为视频形式更能方便分享和传播。本文将介绍两种ppt录制视频的方法,每一种方法都将有详细的步骤和简要的介绍,通过这些方法,你可以轻松将ppt制作成视频&#xf…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《面向平稳氢气需求的综合制氢系统鲁棒优化配置方法》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主的专栏栏目《论文与完整程序》 这个标题涉及到针对平稳氢气需求的综合制氢系统鲁棒优化配置方法。让我们逐步解读这个标题的关键要素: 面向平稳氢气需求: 这部分指…

【Spark精讲】一文讲透SparkSQL聚合过程以及UDAF开发

SparkSQL聚合过程 这里的 Partial 方式表示聚合函数的模式,能够支持预先局部聚合,这方面的内容会在下一节详细介绍。 对应实例中的聚合语句,因为 count 函数支持 Partial 方式,因此调用的是 planAggregateWithoutDistinct 方法&a…

Nacos注册

一、简介 Nacos是阿里云开源的一个服务发现、配置管理和服务鉴权平台,它提供了一种更简单、更便捷、更开放的方式来管理服务,帮助开发者快速实现服务的发现、配置的管理、服务的鉴权等功能。Nacos可以帮助开发者轻松管理微服务应用中的服务提供者、服务…

Ubuntu22.04系统安装软件、显卡驱动、cuda、cudnn、pytorch

Ubuntu22.04系统安装软件、显卡驱动、cuda、cudnn、pytorch 安装 Nvidia 显卡驱动安装 CUDA安装 cuDNN安装 VSCode安装 Anaconda 并更换源在虚拟环境中安装 GPU 版本的 PyTorchReference 这篇博文主要介绍的是 Ubuntu22.04 系统中软件、显卡驱动、cuda、cudnn、pytorch 等软件和…

系统启动流程 - 理解modules加载流程

​编辑 Hacker_Albert    202 linux 启动流程module加载 1.启动过程分为三个部分 BIOS 上电自检(POST)引导装载程序 (GRUB2)内核初始化启动 systemd,其是所有进程之父。 1.1.BIOS 上电自检(POST) BIOS stands for…

杰发科技AC7840——EEPROM初探

0.序 7840和7801的模拟EEPROM使用不太一样 1.现象 按照官方Demo,在这样的配置下,我们看到存储是这样的(连续三个数字1 2 3)。 使用串口工具的多帧发送功能 看不出多少规律 修改代码后 发现如下规律: 前四个字节是…

VSCode中的注释标签

2023年12月30日,周六上午 在软件开发中,开发者会使用这些标签来提供关于代码功能、版本信息、作者、API使用说明等方面的额外信息。 这些标签的含义通常是: apiNote: 提供有关API使用的注释或说明。author: 标识代码作者的信息。category: …

【 ATU NXP-SBC 系列 】FS26XX GUI_OTP烧录与模拟操作

1. 概述 FS26XX 为了其安全性需求,针对重要暂存器的配置,使用 one time program 的功能,避免不小心修改重要暂存器,导致发生重大意外,使系统丧失功能安全性。FS26XX 也可以让使用者先测试 OTP 后的结果功能&#xff0…

Python:将print内容写入文件

简介:print函数是Python中使用频率非常非常高的函数,其包含四个参数:sep、end、file、flush。 历史攻略: Python基础:输入、输出 Python:将控制台输出保存成文件 参数解析: print()函数可以…

简单了解SQL堆叠注入与二次注入(基于sqllabs演示)

1、堆叠注入 使用分号 ; 成堆的执行sql语句 以sqllabs-less-38为例 ?id1 简单测试发现闭合点为单引号 ?id1 order by 3 ?id1 order by 4使用order by探测发现只有三列(字段数) 尝试简单的联合注入查询 ?id-1 union select 1,database(),user()-…

爬虫工作量由小到大的思维转变---<第三十五章 Scrapy 的scrapyd+Gerapy 部署爬虫项目>

前言: 项目框架没有问题大家布好了的话,接着我们就开始部署scrapy项目(没搭好架子的话,看我上文爬虫工作量由小到大的思维转变---<第三十四章 Scrapy 的部署scrapydGerapy>-CSDN博客) 正文: 1.创建主机: 首先gerapy的架子,就相当于部署服务器上的;所以…

[mysql 基于C++实现数据库连接池 连接池的使用] 持续更新中

目背景 常见的MySQL、Oracle、SQLServer等数据库都是基于C/S架构设计的,即(客户端/服务器)架构,也就是说我们对数据库的操作相当于一个客户端,这个客户端使用既定的API把SQL语句通过网络发送给服务器端,MyS…

【Bootstrap学习 day4】

Bootstrap5 列表组 使用Bootstrap创建列表 可以创建三种不类型的HTML列表: 无序列表—顺序无关紧要的项目列表。无序列表中的列表标有项目符号,例如。、等ul>li有序列表—顺序确实很重要的项目列表。有序列表中的列表项用数字标记,例如1、…

欧洲十大跨境电商平台,自养号测评下单的重要性及优势

在欧洲站,用户体量非常庞大,这与近几年人们的消费习惯密不可分,越来越多的人开始网购,据欧盟委员的最新调研显示,在欧盟,近一半(42%)的中小企业通过在线市场销售产品和服务。 所以,逸居海外给大…

Grafana无法发送告警消息的飞书webhook(机器人)

1.问题描述 Grafana无法向飞书机器人发送报警消息,实测使用Grafana自带的webhook也不好使,对于用飞书办公的程序猿非常不便,后来发现一个报警神器,开源免费,关键是好用 PrometheusAlert 2.PrometheusAlert安装 Prom…

ansible_角色的使用

本章主要介绍ansible中角色的使用 了解什么是角色独立地写一个角色使用角色系统自带角色地使用 1.了解角色 正常情况下,配置一个服务如 apache时,要做一系列的操作:安装、拷贝、启动服务等。如果要在不同的机器上重复配置此服务,需要重新执…

企业私有云容器化架构

什么是虚拟化: 虚拟化(Virtualization)技术最早出现在 20 世纪 60 年代的 IBM 大型机系统,在70年代的 System 370 系列中逐渐流行起来,这些机器通过一种叫虚拟机监控器(Virtual Machine Monitor,VMM&#x…

IC入门必备!数字IC中后端设计实现全流程解析(1.3万字长文)

吾爱IC社区自2018年2月份开始在公众号上开始分享数字IC后端设计实现相关基础理论和实战项目经验,累计输出文字超1000万字。全部是小编一个个字敲出来的,绝对没有复制粘贴的情况,此处小编自己得给自己鼓鼓掌鼓励下自己。人生不要给自己设限&am…

【华为数据之道学习笔记】7-5通过感知能力推进企业业务数字化

感知数据在华为信息架构中的位置 感知可以应用于广泛的物理世界和数字世界,感知范围可以从人、物、作业、地点扩展到复杂环境。成熟的用例倾向于以物和人为中心。而在企业中,只有将感知数据纳入整体的数据体系中,才能发挥感知数据的价值。 华…