网络编程2—— UDP Socket实现的 客户端服务器通信完整代码(详细注释帮你快速理解)

news/2024/11/23 13:47:27/

文章目录

  • 前言
  • 一、理论准备
    • Socket套接字是什么
    • UDP协议的特点
  • 二、UDP数据报套接字 提供的API
    • DatagramSocket API
    • DatagramPacket API
  • 三、代码实现请求响应式 客户端服务器
    • 服务器
    • 客户端
    • 疑惑解答
      • 为什么服务器进程需要手动指定端口号而客户端进程不需要
      • 为什么用空的数据报接收网卡中的数据
      • 为什么客户端中的服务器IP与端口号是"127.0.0.1" 与 9090
      • 为什么客户端与服务器都没有调用close方法
  • 总结


前言

本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下!

今天分享的内容是UDP数据报套接字实现的客户端与服务器,一定要理解 DatagramSocket,DatagramPacket 这两个类的作用以及方法,十分有助于你理解服务器,客户端代码。


一、理论准备

Socket套接字是什么

Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。

程序猿👨‍💻编写网络程序,主要编写的是 应用层的程序代码 ,但是真正想要发送或接收数据,都是要 通过应用层调用传输层

因此传输层就为应用层(为我们编写代码)提供了一组api统称为
Socket api

简单来说,这一组api是提供给咱们 编写网络程序使用的接口 用来发送 / 接收网络数据使用的接口

Socket套接字主要针对传输层协议划分为如下三类:
1️⃣ 数据报套接字:使用传输层UDP协议 (本文重点讲解)
2️⃣ 流套接字:使用传输层TCP协议 (下篇文章重点讲解)
3️⃣原始套接字(不做介绍)


UDP协议的特点

特点说明
无连接不刻意保存对端的相关信息
不可靠传输不关心对端是否收到数据,不关注传输数据是否成功
面向数据报以一个UDP数据报为基本单位
大小受限一次最多传输64k
全双工一条通信路径,双向通信。(可以同时发送和接收数据)

二、UDP数据报套接字 提供的API

DatagramSocket API

DatagramSocket 是UDP Socket, 用于发送和接收UDP数据报

Socket对象可以理解为一个遥控器,指挥网卡设备实施各种操作

DatagramSocket构造方法方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(intport)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口号(intport)(一般用于服务端)
DatagramSocket方法方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacketp)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

DatagramPacket API

DatagramPacket 是UDP Socket发送和接收的 数据报

DatagramPacket对象是 数据报

DatagramPacket构造方法方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[]buf, int offset, int length,SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从offset(第二个参数offset)下标开始,数据报中的数据大小即共接收length个字节(第三个参数length)。address(第四个参数address)指定目的主机的IP和端口号
DatagramPacket方法方法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据

三、代码实现请求响应式 客户端服务器

服务器

// 服务器
public class UdpEchoServer {// 需要先定义一个socket对象// 网络通信,必须要使用socket对象private DatagramSocket socket = null;// 绑定一个端口号不一定会成功// 如果该端口号已被别的进程占用或绑定,此时的绑定操作就会抛出异常// 同一个主机,一个端口号,同一时刻,只能被一个进程绑定public UdpEchoServer(int port) throws SocketException {// 构造socket的同时指定要关联/绑定的端口号// socket对象==遥控器,与网卡进行交互的遥控器socket = new DatagramSocket(port);}// 服务器的主逻辑public void start() throws IOException {System.out.println("服务器启动");while (true) {// 每次循环,要做三件事// 1.读取客户端发来的请求并解析// 构造一个空的数据报DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);// 从网卡中读取数据存入 手动构造的空的数据报// 如果网卡中没有数据则会阻塞等待socket.receive(requestPacket);System.out.println("客户端上线");//    为了方便处理请求,将请求转换为字符串String request = new String(requestPacket.getData(),0,requestPacket.getLength());// 2.根据请求计算响应String response = process(request);// 3.把响应结果写回到客户端// 根据response字符串构造一个数据报// 和请求 packet 不同, 此处构造响应数据报的时候, 需要指定这个包要发给谁.DatagramPacket responsePacker = new DatagramPacket(response.getBytes(),response.getBytes().length,// requestPacket 是从客户端这里收来的. getSocketAddress 就会得到客户端的 ip 和 端口requestPacket.getSocketAddress());// 发送到客户端socket.send(responsePacker);// 分别打印:ip,端口号,请求内容,响应内容System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),requestPacket.getPort(), request, response);}}// 这个方法是根据请求计算响应// 有具体业务,就写相关逻辑// 这是服务器中的重要环节public String process(String request) {// 测试客户端与服务器是否网络通信成功// 并无逻辑需求// 直接将请求作为响应返回// 证明通信成功return request;}// 运行main方法,启动服务器public static void main(String[] args) throws IOException {// 实例化服务器对象UdpEchoServer udpEchoServer = new UdpEchoServer(9090);// 启动主逻辑udpEchoServer.start();}
}

客户端

// 客户端
public class UdpEchoClient {// 需要先定义一个socket对象// 网络通信,必须要使用socket对象private DatagramSocket socket = null;private String serverIP;// 服务器IPprivate int serverPort;// 服务器端口号public UdpEchoClient(String serverIP, int serverPort) throws SocketException {// 对于客户端来说, 不需要显示关联端口.// 不代表没有端口, 而是系统自动分配了个空闲的端口.this.socket = new DatagramSocket();this.serverIP = serverIP;this.serverPort = 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, 并进行发送.DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,// 数据报的目的IP与目的端口号InetAddress.getByName(serverIP),serverPort);// InetAddress.getByName这个静态方法可以将代表IP的字符串转换为IP// 向服务器发送数据报socket.send(requestPacket);// 3. 客户端尝试读取服务器返回的响应// 构造一个空的数据报DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);// 从网卡中读取数据存入 手动构造的空的数据报// 如果网卡中没有数据则会阻塞等待socket.receive(responsePacket);// 4. 把响应数据转换成 String 显示出来.String response = new String(responsePacket.getData(),0,responsePacket.getLength());System.out.printf("req: %s, resp: %s\n", request, response);}}// 运行main方法,启动客户端public static void main(String[] args) throws IOException {// 实例化客户端对象UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);// 启动客户端主逻辑udpEchoClient.start();}
}

通信结果:
在这里插入图片描述

疑惑解答

为什么服务器进程需要手动指定端口号而客户端进程不需要

服务器的功能是用来处理其他客户端发来的请求,因此需要为客户端提供自己的端口号,方便客户端进行访问。

虽然服务器要给客户端一个响应,但是客户端的IP地址与端口号都可以在客户端发来请求的数据报中获得,因此客户端不需要手动指定端口号


为什么用空的数据报接收网卡中的数据

一般情况下,一般是使用 参数 作为 “输入”,用返回值作为方法的 “输出”

而用来读取数据的reveve方法却没有返回值。
在这里插入图片描述
因此需要传入一个引用类型的参数,通过这个方法改变这个参数,此时这个参数就是返回值,这个参数也被称为 输出型参数

因此这个方法就是 以参数为返回值


为什么客户端中的服务器IP与端口号是"127.0.0.1" 与 9090

127.0.0.1 是主机环回地址。主机环回是指地址为 127.0.0.1 的任何数据包都不应该离开计算机(主机),发送它——而不是被发送到本地网络或互联网,它只是被自己“环回”,并且发送数据包的计算机成为接收者。

端口号是9090是因为是随意指定的,当然也有一些特殊端口号被指定分配给了一些牛逼的程序。


为什么客户端与服务器都没有调用close方法

DatagramSocket对象也会产生文件描述符,如果如果文件描述符表满了会产生文件资源泄露的严重bug,那么为什么此处没有调用close方法???

原因是,无论客户端进程还是服务器进程, 在他的生命周期中,只需要用到一个DatagramSocket对象 ,而他需要调用close方法的时候也正是他进程结束的时候,此时会自动释放对象也就相当于调用了close方法,因此可以手动调用close方法,但是也可以不调用。


总结

以上就是今天要分享的内容,本文介绍了Socket套接字,以及使用UDP协议的特点以及UDP数据报套接字实现的客户端与服务器。网络编程让我愈发感觉到了编程的魅力,也让我领略到了科技的神奇。各位加油!

路漫漫不止修身,也养性。


http://www.ppmy.cn/news/740659.html

相关文章

【NX】NX二次开发中自动选择当前实体和方向

在NX的二次开发中,我们经常需要选择实体和方向,如果每次手动选择,势必会影响调试的效率,那么有没有办法,让程序一启动就自动选择当前实体和方向呢,自然是有的。 经过我一番研究,因为可能有多个实…

世界上第一个手机是怎么诞生的?谁是第一个用手机的人?

世界上第一部手机是由RudyKrolopp设计,摩托罗拉生产的DynaTAC8000X。 在1972年,Martincooper(手机之父)认命Krolopp为项目组长,让他带着团队负责研发世界上第一部移动电话DynaTAC8000X的开发工作。 在1972年的12月&a…

云计算机概念谁提出的,云计算概念最早为谁提出?

亚马逊推出的计算云服务,1983年,太阳电脑(Sun Microsystems)提出“网络是电脑”(“The Network is the Computer”),2006年3月,亚马逊(Amazon)推出弹性计算云(Elastic Compute Cloud;EC2)服务。 云计算是通过使计算分布在大量的分…

手机麦克风结构原理图_让你一次看懂手机芯片的工作原理

原标题:让你一次看懂手机芯片的工作原理 在《解析通讯技术(上)》与《解析通讯技术(下)》中,我们了解到无线通讯的频谱有限,分配非常严格,相同频宽的电磁波只能使用一次,为了解决僧多粥少的难题,工程师研发出许多“调变技术”(Modulation)与“多工技术”(Multiplex),来增…

ESP32设备驱动-HTU31温湿度传感器驱动

HTU31温湿度传感器驱动 文章目录 HTU31温湿度传感器驱动1、HTU31介绍2、硬件准备3、软件准备4、驱动实现1、HTU31介绍 高性能 HTU31 湿度和温度组合传感器是市场上最小和最精确的湿度传感器之一。 HTU31 提供数字和模拟版本,即使在最恶劣的环境中也能提供快速响应时间、精确测…

小米诚意之作:骁龙8Gen1+IMX766+OIS+120Hz,已跌至两千元档

今天给大家介绍一部小米的诚意之作。在同价位的参数中很抢眼,有特色。废话不多说,数据说话! 本期主角:小米 12 处理器:高通骁龙8 Gen1 相机:后置50MPIMX766主镜头13MP超广角镜头5MP长焦微距镜头 前置3200万…

提升生产效率的关键:如何选择适合您企业的设备管理系统?

在现代工业生产中,设备管理对于提升生产效率和降低成本至关重要。一个高效的设备管理系统可以帮助企业实现设备的有效监控、维护和优化,从而提高设备的可靠性、降低停机时间,并最终提升生产效率。选择适合企业的设备管理系统可能是一个复杂的…

硬件学习笔记(器件篇)—— 铝电解电容(二)

文章目录 铝电解电容的基本参数1.容量和耐压2.封装3.损耗角:损耗角正切值3.工作温度与使用寿命(简述,后面会单独讲)4.漏电流5.最大纹波电流(是一个有效值rms) 结合绿宝石的一个手册分析1.使用温度范围2.电容…