【JavaEE初阶系列】——网络编程 UDP客户端/服务器 程序实现

news2024/11/28 6:36:57

目录

🚩UDP和TCP之间的区别

🎈TCP是有连接的 UDP是无连接的

🎈TCP是可靠传输 UDP是不可靠传输

🎈TCP是面向字节流 UDP是面向数据报

 🎈TCP和UDP是全双工

👩🏻‍💻UDP的socket api使用

💻DatagramSocket API

💻DatagramSocket 方法

💻DatagramPacket API

💻InetSocketAddress API

🎓UDP客户端/服务器 通信程序实现

🔴服务器

📝接收客户端发来的请求,并且解析

📝根据请求返回响应

📝创建一个DatagramPacket类, 存入数据,并发送给客户端

📝打印日志

🔴客户端 


通过网络,让俩个主机之间进行通信,基于这样的通信来完成一定的功能。

进行网络编程的时候,需要操作系统给咱们提供一组api,通过这种api才能完成编程。

api可以认为是应用层和传输层之间交互的路径。socket api就相当于插座一样,通过这一套socket api可以完成不同软件之间,不同操作系统之间的通信。


传输层,提供的协议,主要有2个,TCP和UDP这俩个。TCP和UDP这俩个协议的特性(工作原理) 差异很大,导致,使用俩种协议进行网络编程,也存在一定差别,系统就分别提供俩种api。


🚩UDP和TCP之间的区别

🎈TCP是有连接的 UDP是无连接的

TCP是有连接的,UDP是无连接的。什么是有连接和无连接呢?此处的连接本质上就是建立连接的双方,各自保存对方的信息,俩台计算机建立连接,就是双方彼此保存了对方的关键信息。TCP要想通信,就需要先建立连接(所谓连接就是保存对方的信息),做完之后,才能后续通信。(如果A和B建立连接,但是B拒绝了,通信就无法完成了),UDP想要通信,就直接发送数据就行了,不需要建立连接,也就是不需要保存对方的信息。虽然UDP本身是不保存的,但是你调用的UDP的socket api的时候要把对方的位置啥的给传过去。后面再 实现TCP就需要在类里初始化IP地址和端口号传入构造方法中,而UDP不用。


🎈TCP是可靠传输 UDP是不可靠传输

TCP里面内置了可靠传输的机制,UDP没有。网络上进行通信,A发送给B消息,这个消息不可能做到100%发送到的,所以什么是可靠传输呢?可靠传输就是A发送给B发信息,消息是不是到达B这一方,A自己能感知到(A心里有数)进一步的,就可以再发送失败的时候采取一定的措施(尝试重传之类的)。而从不可靠传输到可靠传输都是需要付出一些代价的,比如可靠传输的机制比较复杂,传输效率低。

就像一个老中医生有资历给病人看病,看了一下就知道他有什么毛病,只是不说出来,老中医心里有数,然后就去拿药配方进行医治。


🎈TCP是面向字节流 UDP是面向数据报

字节流和文件操作里面的字节流是一个意思,所以我们再实现TCP客户端和服务器之间的通信的时候,就需要用到文件操作里面字节输入流和输出流。

  • TCP是和文件操作一样,以字节为单位来进行传输
  • UDP是按照数据报为单位,来进行传输的。UDP数据报是有严格的格式的。

 🎈TCP和UDP是全双工

  • 一个信道,允许双向通信,就是全双工
  • 一个信道,只能单向通信,就是半双工

代码中使用一个Socket对象,就可以发送数据也能接收数据。

半双工——单向通信

一个管子,只能一边吹气。

但是B 不能给A吹气 ,这就是所谓的单向通信——半双工

全双工——双向通信

一根网线,电流不是只能从一边流向另一边,咋能双向通信呢?

就比如一个道路,我们中间划一道黄线,双向行驶


👩🏻‍💻UDP的socket api使用

💻DatagramSocket API

DatagramSocket UDP Socket ,用于发送和接收 UDP 数据报。
DatagramSocket 构造方法:
方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务器)

为什么服务器要指定端口呢?而客户端不同?

因为服务器是程序员决定的,知道哪些端口是可以用的,这是可控的,客户端是不可控的,每个用户电脑程序不一样,占用的端口也不一样,如果手动指定端口,会产生冲突。所以 客户端交给系统自动分配的。

 socket其实也是操作系统一个概念,本质上是一种特殊的文件,Socket就属于是把"网卡"这个设备,抽象成了文件了,往socket文件中写数据,就相当于通过网卡发送数据,从socket文件读数据,就相当于通过网卡接收数据。这就是网络通信和文件操作统一了。


💻DatagramSocket 方法

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法就会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

receive()和send()方法里面的参数其实是输出型参数,数据报是空的,然后进行填充,并返回。 


💻DatagramPacket API

DatagramPacket UDP Socket 发送和接收的数据报。
DatagramPacket 构造方法:
方法签名方法说明
DatagramPacket(byte[] buf,int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号

 DatagramPacket 方法:

方法签名方法说明
InetAddress. getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号

byte[] getData() 获取数据报中的数据

构造UDP发送的数据报时,需要传入socketAddress,该对象可以使用InetSocketAddress来创建。


💻InetSocketAddress API

InetSocketAddress SocketAddress 的子类 )构造方法:
方法签名方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

🎓UDP客户端/服务器 通信程序实现

这个程序是没有什么业务逻辑,只是单纯的调用socket api .让客户端给服务器发送一个请求,请求就是一个从控制台输入的字符串,服务器收到字符串之后,也就会把这个字符串原封不动的返回客户端,客户端再显出来。——回显服务器(echo server)


🔴服务器

📝接收客户端发来的请求,并且解析

 public UdpEchoServer(int port) throws SocketException {
        socket=new DatagramSocket(port);//服务器需要指定端口
    }

服务器和客户端都需要创建Socket对象。

  • 服务器的socket一般要显示的指定一个端口号
  • 客户端的socket一般不能显式指定(不显示指定,此时系统会自动分配一个随机的端口)

//接收请求之前,我们需要开辟一个空间存储要求
            DatagramPacket requestSocket=new DatagramPacket(new byte[4096],4096);
            socket.receive(requestSocket);

DatagramPacket这个对象用来承载从网卡这边读到的数据,收到数据的时候,需要搞一个内存空间来保存这个数据,DatagramPacket内部不能自行分配内存空间,因此就需要程序员手动把孔吉纳创建好,交给DatagramPacket进行处理。

服务器一旦启动,就会立即执行到这里的receive方法,此时客户端的请求还没来,这种情况也是没有关系的,receive就会直接阻塞,就会一直阻塞到真正客户端把请求发过来为止。


 //由于接收到的请求是二进制,我们需要转换成字符串
            String request=new String(requestSocket.getData(),0,requestSocket.getLength());

这个getLength 得到的结果是否是上述的4096?这个结果是收到的数据的真实长度(取决于发送方这一次到底发送了多少数据。取这个区间内的字节,构成一个Stirng。


📝根据请求返回响应

 //2.根据请求返回响应
            String response=new String(request);
 public String  process(String request){
        return request;
    }

 这个步骤是一个服务器程序,最核心的步骤!咱们当时是echo server不涉及这些流程,也不必考虑响应怎么计算,只要请求过来,就把请求当作响应。


📝创建一个DatagramPacket类, 存入数据,并发送给客户端

 UDP是无连接的,UDP自身不会保存数据要发给谁,就需要每次发送的时候,重新指定,数据要发到哪里去。上述创建数据报是存入发来的请求。

    //3.创建一个DatagramPacket类, 存入数据,并发送给客户端
    //requestSocket.getSocketAddress() requestSocket里面包含客户端的IP地址和端口号
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),0,response.getBytes().length,requestSocket.getSocketAddress());
            socket.send(responsePacket);

 构造这个数据报,需要指定数据内容,也指定一下数据报要发给谁。再网络传输的时候,肯定是要使用字节的。

  • 可以让response.getBytes().length改成response.length()嘛?不行的,本质上也是和字符集有关系的。如果你这个字符串里内容都是英文字符,此时字节和字符个数是一样的,如果包含中文就不一样的。
  • requestSocket.getSocketAddress()指定一下请求中的地址(数据从哪里来,我们就要到哪里去)包含IP地址和端口号

📝打印日志

//打印一个日志 打印 客户端IP 客户端端口 客户端内容  服务器内容
 System.out.printf("[%s:%d] %s,%s",responsePacket.getAddress(),responsePacket.getPort(),request,response);

package UDP;

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

public class UdpEchoServer {
    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 requestSocket=new DatagramPacket(new byte[4096],4096);
            //然后接收请求(如果客户端还没发来请求,就阻塞等待)
            socket.receive(requestSocket);
            //由于接收到的请求是二进制,我们需要转换成字符串
            String request=new String(requestSocket.getData(),0,requestSocket.getLength());
        //2.根据请求返回响应
            String response=new String(request);

        //3.创建一个DatagramPacket类, 存入数据,并发送给客户端
            //requestSocket.getSocketAddress() requestSocket里面包含客户端的IP地址和端口号
            DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),0,response.getBytes().length,requestSocket.getSocketAddress());
            socket.send(responsePacket);

          //打印一个日志 打印 客户端IP 客户端端口 客户端内容  服务器内容
            System.out.printf("[IP地址:%s 端口号:%d] request=%s response=%s",responsePacket.getAddress(),responsePacket.getPort(),request,response);
        }
    }
    public String  process(String request){
        return request;
    }
    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer=new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}

🔴客户端

客户端做的事情就是 发出请求,尝试得到服务器返回的响应

  • 1.将输入的字符串转换成请求,并发送给服务器
  • 2.发出请求之后,我们就要创建一个数据报,存储由服务器返回的请求,尝试返回请求
  • 3.将响应转换成字符串,并响应出来
package UDP;

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

public class UDPClient {
    DatagramSocket socket=null;
    String serverIp="";
    int serverPort=0;

    public UDPClient(String serverIp, int serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.serverIp = serverIp;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        System.out.println("客户端开始启动");
        Scanner scanner=new Scanner(System.in);
        while (true){
            //1.将输入的字符串转换成请求,并发送给服务器
            String request=scanner.next();
            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIp),serverPort);
            socket.send(requestPacket);

            //2.尝试返回服务器的响应
            DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);

            //3.将响应转换成字符串,并响应出来
            String response=new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        UDPClient udpClient=new UDPClient( "10.41.90.105",9090);
        udpClient.start();
    }
}

🔴服务器/客户端通信流程 


我们写完UDP的服务器和客户端的代码之后,为什么不close?就是关闭数据报套接字

socket也是文件,不关闭就会出问题了,就会出现文件资源泄漏嘛?(资源泄漏就是代码中频繁的打卡文件,但是不关闭,在一个进程的运行过程中,不断积累打开的文件,逐渐消耗掉文件描述符表里的内容,最终就消耗没了,如果进程的生命周期很短,打开一下没多久就关闭了,谈不上泄漏)

socket是文件描述符表中的一个表项,每次打开一个文件,就要占用一个位置。文件描述符,是在pdb上的。(跟随进程的)

这个socket在整个程序运行过程中都是需要使用的(不能提前关闭)当socket不需要使用的时候,意味着程序就要结束了,进程结束,此时随之文件描述符就会销毁了(pcb都销毁了)。随着销毁的过程,被系统自动回收了。

先启动服务器,再启动客户端

 


🍭 基于echo server写一个翻译服务器 

请求的是一个英文单词,响应就会返回对应的中文翻译。

cat——》小猫

dog——》小狗

通过代码来实现


public class UdpDicServer extends UdpEchoServer {
    private Map<String,String> dict=new HashMap<>();
    public UdpDicServer(int port) throws SocketException {
        super(port);
    }
}

用哈希表 来记录键值对,key是英文单词,value是英文单词的翻译,构成了键值对。

然后我们继承了服务器的类,我们要先调用他的构造方法。然后进行初始化。

package UDP;

import javax.rmi.CORBA.Util;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

public class UdpDicServer extends UdpEchoServer {
    private Map<String,String> dict=new HashMap<>();
    public UdpDicServer(int port) throws SocketException {
        super(port);
        //此时往这个表中插入几千几万个英文单词
        dict.put("cat","猫");
        dict.put("dog","狗");
        dict.put("flower","花");
    }
    //重写process方法,再重写的方法中完成翻译的过程
    //翻译的本质是“查表"
    public String  process(String request){
        return dict.getOrDefault(request,"该词在该词典中不存在");
    }

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

start方法中,调用process方法,this.process。

当前是子类引用调用start,this就是指向子类引用,虽然this是父类的类型,但是实际指向的是子类引用,调用process自然就会执行到子类的方法中, 上述重写了process方法,就可以在子类中组织你想要的”业务逻辑“。


只要跑得足够久,所有的雨都是阵雨。 

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

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

相关文章

【muzzik 分享】3D模型平面切割

# 前言 一年一度的征稿到了&#xff0c;倒腾点存货&#xff0c;3D平面切割通常用于一些解压游戏里&#xff0c;例如水果忍者&#xff0c;切菜这些&#xff0c;今天我就给大家讲讲怎么实现3D切割以及其原理&#xff0c;帮助大家更理解3D中的 Mesh(网格)&#xff0c;以及UV贴图和…

ssm+vue的实验室课程管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的实验室课程管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

【NLP笔记】大模型微调方法概述

对于一些生成式场景而言&#xff0c;没有固定的回答结果&#xff0c;采用AI Agent的增强范式&#xff0c;可以极大地提升模型生成的效果。但是对于有固定格式、输出目标的场景而言&#xff0c;仅从prompt优化的角度出发很难突破瓶颈&#xff0c;需要通过微调来提升效果&#xf…

背 单 词 (考研词汇闪过)

单词&#xff1a; 买考研词汇闪过 研究艾宾浩斯遗忘曲线 https://www.bilibili.com/video/BV18Y4y1h7YR/?spm_id_from333.337.search-card.all.click&vd_source5cbefe6dd70d6d84830a5891ceab2bf9 单词方法 闪记背两排&#xff08;5min&#xff09;重复一遍&#xff08;2mi…

vue中预览docx、xlsx、pptx、pdf

前言&#xff1a;其实本来是要做全类型文件预览的&#xff0c;但是一直找不到合适的doc,xlx,ppt预览插件。要是有可以使用的&#xff0c;可以评论推荐给我 我使用的node版本&#xff1a;v18.19.1 参考官网&#xff1a;preview 文件预览 | ran 引入方式&#xff1a; //安装组…

Flask快速搭建文件上传服务与接口

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、需求背景 前端通过浏览器&#xff0c;访问后端服务器地址&#xff0c;将目标文件进行上传。 访问地址&#xff1a;http://127.0.0…

✔ ★Java项目——设计一个消息队列(二)

Java项目——设计一个消息队列 四. 项⽬创建五. 创建核⼼类创建 Exchange&#xff08;名字、类型、持久化&#xff09;创建 MSGQueue&#xff08;名字、持久化、独占标识&#xff09;创建 Binding&#xff08;交换机名字、队列名字、bindingKey用于与routingKey匹配&#xff09…

前端docker jenkins nginx CI/CD持续集成持续部署-实战

最近用go react ts开发了一个todolist后端基本开发完了,前端采用CI/CD方式去部署。 步骤总结 先安装docker 和 docker-compose。安装jenkins镜像,跑容器的时候要配好数据卷。配置gitee或github(我这里使用gitee)在服务器上一定要创建好dokcer的数据卷,以便持久保存jenkin…

【MySQL】锁篇

SueWakeup 个人主页&#xff1a;SueWakeup 系列专栏&#xff1a;学习技术栈 个性签名&#xff1a;保留赤子之心也许是种幸运吧 本文封面由 凯楠&#x1f4f8;友情提供 目录 本系列专栏 1. MySQ 中的锁 2. 表锁和行锁 表锁 行锁 3. InnoDB 存储引擎的三种行级锁 4. 悲观锁…

怎么开发一个预约小程序_一键预约新体验

预约小程序&#xff0c;让生活更便捷——轻松掌握未来&#xff0c;一键预约新体验 在快节奏的现代生活中&#xff0c;我们总是在不断地奔波&#xff0c;为了工作、为了生活&#xff0c;不停地忙碌着。然而&#xff0c;在这繁忙的生活中&#xff0c;我们是否曾想过如何更加高效…

探探各个微前端框架

本文作者为 360 奇舞团前端开发工程师 微前端架构是为了在解决单体应用在一个相对长的时间跨度下&#xff0c;由于参与的人员、团队的增多、变迁&#xff0c;从一个普通应用演变成一个巨石应用(Frontend Monolith)后&#xff0c;随之而来的应用不可维护的问题。这类问题在企业级…

点击按钮(文字)调起elementUI大图预览

时隔一年&#xff0c;我又回来了 ~ 最近在做后台&#xff0c;遇到一个需求&#xff0c;就是点击“查看详情”按钮&#xff0c;调起elementUI的大图预览功能&#xff0c;预览多张图片&#xff0c;如下图&#xff1a; 首先想到的是使用element-ui的el-image组件&#xff0c;但它是…

Towards Geolocation of Millions of IP Addresses(2012年)

下载地址: Towards geolocation of millions of IP addresses | Proceedings of the 2012 Internet Measurement Conference 被引用次数:70 Hu Z, Heidemann J, Pradkin Y. Towards geolocation of millions of IP addresses[C]//Proceedings of the 2012 Internet Measure…

利用Python实现可视化交互界面:Dash

Dash是一个低代码数据框架&#xff0c;用Python实现可视化交互界面&#xff0c;不用写Javascript&#xff0c;开源&#xff0c;支持回调、HTML组件等功能。 安装 pip install dash使用 # Import packages from dash import Dash, html, dash_table, dcc, callback, Output, …

[BT]BUUCTF刷题第14天(4.10)

第14天&#xff08;共5题&#xff09; Web [BJDCTF2020]ZJCTF&#xff0c;不过如此 打开网站直接显示源代码&#xff1a; <?php error_reporting(0); //关闭报错 $text $_GET["text"]; $file $_GET["file"]; if(isset($text)&&(file…

C#如何用NPOI创建、读取、更新Excel文件

一.获取引用NPOI VS2017&#xff0c;通过Nuget工具包下载NPOI到指定的项目中&#xff0c;如下 二.添加如下命名空间,其中HSSF是操作*.xls文件&#xff0c;XSSF操作*.xlsx文件. using NPOI; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using NPOI.HSSF.UserModel; …

Debian 安装 Docker

Debian 安装 Docker。 这是官方安装文档 Install Docker Engine on Debian | Docker DocsLearn how to install Docker Engine on Debian. These instructions cover the different installation methods, how to uninstall, and next steps.https://docs.docker.com/engine/i…

如何将普通maven项目转为maven-web项目

文件-项目结构&#xff08;File-->Project Structure &#xff09; 模块-->learn&#xff08;moudle-->learn&#xff09; 选中需要添加web的moudle&#xff0c;点击加号&#xff0c;我得是learn&#xff0c;单击选中后进行下如图操作&#xff1a; 编辑路径 结果如下…

【网站项目】英语学习激励系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

神经网络背后的数学原理

原文地址&#xff1a;The Math Behind Neural Networks 2024 年 3 月 29 日 深入研究现代人工智能的支柱——神经网络&#xff0c;了解其数学原理&#xff0c;从头开始实现它&#xff0c;并探索其应用。 神经网络是人工智能 &#xff08;AI&#xff09; 的核心&#xff0c;为…