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

目录

一.什么是回显服务器?

二.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 就可以了。大家可以尝试一下!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NModbus-一个C#的Modbus协议库实现

NModbus-一个基于C#实现的Modbus通信协议库 最近在学习C#的时候,因为之前做过环保设备时使用C做过环保设备采集使用到了Modbus协议,当时看了一下基于C语言开发的libmodbus库。所以特意搜索看了一下C#下有什么Modbus协议库,在Github上面找了一…

Spring Boot学习随笔- 集成MyBatis-Plus,第一个MP程序(环境搭建、@TableName、@TableId、@TableField示例)

学习视频:【编程不良人】Mybatis-Plus整合SpringBoot实战教程,提高的你开发效率,后端人员必备! 引言 MyBatis-Plus是一个基于MyBatis的增强工具,旨在简化开发,提高效率。它扩展了MyBatis的功能,提供了许多实用的特性,…

数据缓存(Redis, Spring Cache)——后端

场景:给用户端展示的数据都是通过查询数据库所得,因此数据库访问压力会随着用户访问量增大而增加,从而导致系统响应慢、用户体验差。 方法:通过Redis缓存数据,减少查询数据库操作。(Redis的数据是存储在内存…

vue中常见的指令

简单介绍一下常见的vue中用到的指令 v-on 指定当前的事件&#xff0c;语法糖为&#xff0c;如例子所示&#xff0c;指定按钮的事件为addCounter&#xff0c;点击会使变量counter 1 <!DOCTYPE html> <html><head><meta charset"utf-8" />…

【教学类-43-02】20231226 九宫格数独2.0(n=9)(ChatGPT AI对话大师生成 回溯算法)

作品展示&#xff1a; 背景需求&#xff1a; 大4班20号说&#xff1a;我不会做这种&#xff08;九宫格&#xff09;&#xff0c;我做的是小格子的&#xff0c; 他把手工纸翻过来&#xff0c;在反面自己画了矩阵格子。向我展示&#xff1a; “我会做这种&#xff01;” 原来他…

Android MVP 写法

前言 Model&#xff1a;负责数据逻辑 View&#xff1a;负责视图逻辑 Presenter&#xff1a;负责业务逻辑 持有关系&#xff1a; 1、View 持有 Presenter 2、Model 持有 Presenter 3、Presenter 持有 View 4、Presenter 持有 Model 辅助工具&#xff1a;ViewBinding 执行…

C++:模板进阶

C:模板进阶 前言一、非类型模板参数二、模板的特化2.1 概念2.2 类模板特化2.2.1 全特化2.2 偏特化 2.3 函数模板特化 四、模板分离编译4.1 什么是分离编译4.2 模板的分离编译4.3 解决方法 五、总结 前言 在C:模板初阶中&#xff0c;已经介绍过了模板的基本用法&#xff0c;接下…

三角形的外心、内心、中心、重心、垂心、中心、费马点

三角形的外心 即三角形的外接圆的中心。此时&#xff1a; A O B O C O AO BO CO AOBOCO 需要注意的有以下几点&#xff1a; 第一&#xff0c; A O D &#xff0c; B O E &#xff0c; C O F 不一定是直线&#xff0c;能否证明&#xff1f; \color{red}AOD&#xff0c;BOE…

2023总结与展望--Empirefree

今年一篇博客都没写过了&#xff0c;好像完全在忙在工作和生活上面了&#xff0c;珍惜自我&#xff0c;保持热情&#xff0c;2024对我好点 文章目录 &#x1f525;1. 年终总结1.1.学习工作计划1.2. 生活计划1.3 个人总结 &#x1f525;2. 未来展望 &#x1f525;1. 年终总结 1…

数据库一般会采取什么样的优化方法?

数据库一般会采取什么样的优化方法&#xff1f; 1、选取适合的字段属性 为了获取更好的性能&#xff0c;可以将表中的字段宽度设得尽可能小。 尽量把字段设置成not null 执行查询的时候&#xff0c;数据库不用去比较null值。 对某些省份或者性别字段&#xff0c;将他们定义为e…

Linux - 设置虚拟机和主机IP在同一网段(桥接)

1.查看主机ip地址等相关信息。 ipconfig -all 2.设置虚拟网络编辑器 打开虚拟网络编辑器 设置虚拟网络编辑器&#xff0c;设置为桥接模式。&#xff08;记得以管理员方式打开VMware&#xff09;。 3.修改虚拟机网卡文件 查看虚拟机ip,我们的目标是将其修改为与主机同一网段…

如何在MAC OS中的XCODE下添加 <bits/stdc++.h>

mac上使用的编译器是Clang&#xff0c;但是没有万能头文件bits/stdc.h\&#xff0c;本文介绍如何添加万能头文件 Xcode 版本&#xff1a;15.1 - 打开应用程序-Xcode-右键显示包内容 Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/includ…

Android—— MIPI屏调试

一、实现步骤 1、在kernel/arch/arm/boot/dts/lcd-box.dtsi文件中打开&dsi0节点&#xff0c;关闭其他显示面板接口&#xff08;&edp_panel、&lvds_panel&#xff09; --- a/kernel/arch/arm/boot/dts/lcd-box.dtsib/kernel/arch/arm/boot/dts/lcd-box.dtsi-5,14 …

Halcon区域生长的几种算法regiongrowing/regiongrowing_mean/watersheds

Halcon区域生长的几种算法 文章目录 Halcon区域生长的几种算法1. regiongrowing 算子2. regiongrowing_mean算子3. 分水岭算法 如果想要获得具有相似灰度的相连区域&#xff0c;可以使用区域生长法寻找相邻的符合条件的像素。区域生长法的基本思想是&#xff0c;在图像上选定一…

22 UVM Callbacks

回调用于改变组件或对象的行为而不修改其代码。请参阅SystemVerilog callback - VLSI Verify以更好地理解。UVM 中的phasing机制就是回调的一个简单示例。 1 UVM Callback Usage 允许即插即用机制以建立可重用的验证环境。基于钩子方法&#xff08;hook method&#xff09;调…

2023-12-29 服务器开发-centos-安装php8

摘要: 2023-12-29 服务器开发-centos-安装php8 centos-安装php8 必备条件 Minimal CentOS 8 / RHEL 8User with sudo rightsInternet Connection (1) 更新系统 更新系统 $ sudo dnf update $ sudo dnf upgrade 重启系统 $ sudo reboot (2) 启用 EPEL & Remi 软件库…

golang 图片加水印,字体文件从哪里找

鼠标左键双击此电脑图标在此电脑文本框输入电脑默认字体地址&#xff1a;C:\Windows\Fonts找到需要用到的字体文件&#xff0c;复制到指定文件夹

Python/R/GUI/BI类型常用数据可视化工具

什么是数据可视化工具&#xff1f; 数据可视化工具是指旨在可视化数据的所有形式的软件。它们处理数据输入&#xff0c;将其转换为用户可以根据自己的需求进行定制的视觉效果。 不同的工具可以包含不同的功能&#xff0c;但最基本的是&#xff0c;数据可视化工具提供输入数据集…

新品出击 | 软网关BLIoTLink免费发布

新品出击|软网关BLIoTLink免费发布 BLIoTLink是一款免费的物联网协议转换软件&#xff0c;可以部署在任何基于Linux OS的系统&#xff08;Linux、Debian、Ubuntu、FreeRTOS、RT-Thread&#xff09;中&#xff0c;使用灵活&#xff0c;可以实现数据的采集以及接入网络平台。 BL…

[Angular] 笔记 18:Angular Router

Angular Router 视频 chatgpt&#xff1a; Angular 具有内置的大量工具、功能和库&#xff0c;功能强大且经过良好设计&#xff0c;如组件化架构、依赖注入、模块化系统、路由和HTTP客户端等。这些功能可以直接用于项目中&#xff0c;无需额外的设置或第三方库。这简化了开发流…