UDP客户端、服务端及简易聊天室实现 —— Java

UDP 协议(用户数据包协议)
UDP 是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接,简单来说,当客户端向接收端发送数据时,客户端不会确认接收端是否存在,就会发出数据。同样接收端在接收数据时,也不会向发送端反馈是否收到数据
由于使用 UDP 协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据传输
例如:视频会议通常采用 UDP 协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用 UDP 协议传送数据时,由于 UDP 的面向无连接性,不能保证数据的完整性,因此在传输重要数据是不建议使用 UDP 协议

客户端与服务器端图解

TCP 是基于字节流的传输层通信协议,所以 TCP 编程是基于 IO 流编程

Java:实现UDP协议发送/接收数据

UDP 通信编程
发送 数据
1..创建 DatagramSocket 对象
不传参,指定端口。(发送端口)
2.创建 DatagramPacket 对象
需传入指定的byte数组,长度,要发送的地址,接收的端口号。
3.发送数据
4.关闭

 客户端:

package com.lpy.socketdemo2;import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;public class SendDemo {public static void main(String[] args) throws IOException {// 创建 DatagramSocket 对象   DatagramSocket ds = new DatagramSocket();String str = "hello world";byte[] bytes = str.getBytes();InetAddress address = InetAddress.getByName("127.0.0.1");int port = 80;// 创建 DatagramPacket 对象 DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);// 发送数据ds.send(dp);// 关闭 ds.close();}
}
 接收 数据
  1. 创建 DatagramSocket 对象,指定接收用的端口号
  2. 创建 DatagramPacket 对象,用byte数组接收,指定接收长度
  3. 接收数据
  4. 解析数据

服务端:

package com.lpy.socketdemo3;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;public class ReceiveDemo {public static void main(String[] args) throws IOException {// 创建 DatagramSocket 对象,指定接收用的端口号DatagramSocket ds = new DatagramSocket(10086);byte[] bytes = new byte[1024];// 创建 DatagramPacket 对象,用byte数组接收,指定接收长度DatagramPacket dp = new DatagramPacket(bytes, bytes.length);// 接收数据ds.receive(dp);// 解析数据byte[] data = dp.getData();int len = dp.getLength();InetAddress address = dp.getAddress();int port = dp.getPort();System.out.println("接收到的数据" + new String(data, 0, len)); // // 接收到的数据hello worldSystem.out.println("该数据是从" + address + "这台电脑中的" + port + "端口发送出来的"); // // 该数据是从/127.0.0.1这台电脑中的65220端口发送出来的// 关闭ds.close();}
}

DatagramSocket类负责接收和发送数据报。每个DatagramSocket对象都会与一个本地端口绑定,在此端口监听发送过来的数据报。在客户程序中,一般由操作系统为DatagramSocket类分配本地端口,这种端口也被称为匿名端口。在服务器程序中,一般由程序显式地为DatagramSocket类指定本地端口。


send()方法可用于发送数据包,DatagramSocket的receive()方法负责接收一个数据报
值得注意的是,UDP提供不可靠的传输,如果数据报没有到达目的地,那么send()方法不会抛出任何异常,发送方程序就无法知道数据报是否被接收方接收到,除非双方通过应用层的特定协议来确保接收方未收到数据报时,发送方能重发数据报。 send()方法可能会抛出IOException,但是与java.uti.Socket相比,DatagramSocket的send()方法抛出IOException的可能性很小。如果发送的数据报超过了底层网络所支持的数据报的大小,就可能会抛出SocketException,它是IOException的子类。

在使用UDP实现Socket通信时,服务端与客户端都是使用DatagramSocket类,传输的数据要存放在DatagramPacket类中。 DatagramSocket类表示用来发送和接收数据报包的套接字。数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。在DatagramSocket上总是启用UDP广播发送。为了接收广播包,应该将DatagramSocket绑定到通配符地址。在某些实现中,将DatagramSocket绑定到一个更加具体的地址时广播包也可以被接收。

DatagramPacket类表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。 DatagramSocket类中的public synchronized void receive(DatagramPacket p)方法的作用是从此套接字接收数据报包。当此方法返回时,DatagramPacket的缓冲区填充了接收的数据。数据报包也包含发送方的IP地址和发送方机器上的端口号,此方法在接收到数据报前一直阻塞。数据报包对象的length字段包含所接收信息的长度。如果发送的信息比接收端包关联的byte[]长度长,该信息将被截短。如果发送信息的长度大于65507,则发送端出现异常。
DatagramSocket类中的public void send(DatagramPacket p)方法的作用是从此套接字发送数据报包。
DatagramPacket包含的信息有:将要发送的数据及其长度、远程主机的IP地址和远程主机的端口号。 DatagramPacket类中的public synchronized byte[]getData()方法的作用是返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量offset处开始,持续length长度。

在某些场合,一个DatagramSocket可能只希望与固定的另一个远程DatagramSocket通信。例如,NFS客户只接收来自与之通信的服务器的数据报。再例如,在网络游戏中,一个游戏玩家只接收他的游戏搭档的数据报。 从JDK1.2开始,DatagramSocket添加了一些方法,利用这些方法,可以使一个DatagramSocket只能与另一个固定的DatagramSocket交换数据报。
(1)public void connect(InetAddress host,int port) connect()方法实际上不建立TCP意义上的连接,但它能限制当前DatagramSocket只对参数指定的远程主机和UDP端口收发数据报。如果当前DatagramSocket试图对其他的主机或UDP端口发送数据报,send()方法就会抛出IllegalArgumentException。从参数以外的其他主机或UDP端口发送过来的数据报则被丢弃,程序不会得到任何通知,也不会抛出任何异常。
2)public void disconnect() disconnect()中止当前DatagramSocket已经建立的“连接”,这样,DatagramSocket就可以再次对任何其他主机和UDP端口收发数据报。
(3)public int getPort() 当且仅当DatagramSocket已经建立连接时,getPort()方法才返回DatagramSocket所连接的远程UDP端口,否则返回“-1”。
(4)public InetAddress getInetAddress() 当且仅当DatagramSocket已经建立连接时,getInetAddress()方法才返回DatagramSocket所连接的远程主机的IP地址,否则返回null。
(5)public SocketAddress getRemoteSocketAddress() 当且仅当DatagramSocket已经建立连接时,getRemoteSocketAddress()方法才返回一个SocketAddress对象,表示DatagramSocket所连接的远程主机以及端口的地址,否则返回null。

UDP客户程序通常只和特定的UDP服务器通信,因此可在UDP客户程序中把DatagramSocket与远程服务器连接。UDP服务器需要与多个UDP客户程序通信,因此在UDP服务器中一般不用对DatagramSocket建立特定的连接。
关闭DatagramSocket
DatagramSocket的close()方法会释放所占用的本地UDP端口。在程序中及时关闭不再需要的DatagramSocket,这是好的编程习惯。

DatagramSocket的选项
1.SO_TIMEOUT选项 ·设置该选项:public void setSoTimeout(int milliseconds) throws SocketException ·读取该选项:public int getSoTimeOut() throws SocketException DatagramSocket类的SO_TIMEOUT选项用于设定接收数据报的等待超时时间,单位为ms,它的默认值为0,表示会无限等待,永远不会超时。以下代码把接收数据报的等待超时时间设为3min:

if(socket.getTimeOut() == 0) socket.setTimeOut(60000*3);

DatagramSocket的setTimeout()方法必须在接收数据报之前执行才有效。当执行DatagramSocket的receive()方法时,如果等待超时,那么会抛出SocketTimeoutException,此时DatagramSocket仍然是有效的,尝试再次接收数据报。

2.SO_RCVBUF选项
·设置该选项:public void setReceiveBufferSize(int size) throws SocketException
·读取该选项:public int getReceiveBufferSize() throws SocketException
SO_RCVBUF表示底层网络的接收数据的缓冲区(简称接收缓冲区)的大小。对于有着较快传输速度的网络(比如以太网),较大的缓冲区有助于提高传输性能,因为可以在缓冲区溢出之前存储更多的入站数据报。与TCP相比,对于UDP,确保接收数据的缓冲区具有足够的大小更为重要,因为当缓冲区满后再到达的数据报会被丢弃。而TCP会在这种情况下要求重传数据,确保数据不会丢失。 此外,SO_RCVBUF还决定了程序接收的数据报的最大大小。在接收缓冲区中放不下的数据报会被丢弃。 setReceiveBufferSize(int size)方法设置接收缓冲区的大小,值得注意的是,许多网络都限定了接收缓冲区大小的最大值,如果参数size超过该值,那么setReceiveBufferSize(int size)方法所做的设置无效。getReceiveBufferSize()方法返回接收缓冲区的实际大小。

3.SO_SNDBUF选项
·设置该选项:public void setSendBufferSize(int size) throws SocketException
·读取该选项:public int getSendBufferSize() throws SocketException SO_SNDBUF表示底层网络的发送数据的缓冲区(简称发送缓冲区)的大小。setSendBufferSize(int size)方法设置发送缓冲区的大小,值得注意的是,许多网络都限定了发送缓冲区大小的最大值,如果参数size超过该值,那么setSendBufferSize(int size)方法所做的设置无效。getSendBufferSize()方法返回发送缓冲区的实际大小。

4.SO_REUSEADDR选项
·设置该选项:public void setResuseAddress(boolean on) throws SocketException
·读取该选项:public boolean getResuseAddress() throws SocketException
SO_REUSEADDR选项对于UDP Socket和TCP Socket有着不同的意义。对于UDP,SO_REUSEADDR决定多个DatagramSocket是否可以同时被绑定到相同的IP地址和端口。如果多个DatagramSocket被绑定到相同的IP地址和端口,那么到达该地址的数据报会被复制给所有的DatagramSocket。 setResuseAddress(boolean on)必须在DatagramSocket绑定到端口之前被调用,这意味着必须采用以下这个构造方法来创建DatagramSocket对象。

protected DatagramSocket(DatagramSocketImpl impl)
//此构造方法创建的DatagramSocket对象未与任何端口绑定

5.SO_BROADCAST选项
·设置该选项:public void setBroadCast(boolean on) throws SocketException
·读取该选项:public boolean getBroadCast() throws SocketException SO_BROADCAST选项决定是否允许对网络广播地址发送广播数据报。对于一个地址为192.168.5.*的网络,其本地网络广播地址为 192.168.5.255。UDP广播常被用于JXTA对等发现协议(JXTA Peer Discovery Protocol)、服务定位协议(Service Location Protocol)和DHCP动态主机配置协议(Dynamic Host Configuration Protocol)等协议。例如,如果需要和本地网中的服务器通信,但是预先不知道服务器的地址,就需要采用这些协议。 广播数据报一般只在本地网络中传播,路由器和网关一般不转发广播数据报。SO_BROADCAST选项的默认值为true。如果不希望发送广播数据报,那么可以调用DatagramSocket的setBroadCast(false)方法。

实现简易的通信聊天

客户端:

package com.lpy.socketdemo4;import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class Send {public static void main(String[] args) throws IOException {DatagramSocket ds = new DatagramSocket();Scanner sc = new Scanner(System.in);int port = 80;InetAddress address = InetAddress.getByName("127.0.0.1");while (true) {System.out.println("请输入发送的内容:");String str = sc.nextLine();byte[] bytes = str.getBytes("UTF-8");DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);ds.send(dp);if (str.equals("bye")) break;}ds.close();}
}

 服务端:

package com.lpy.socketdemo4;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class receive {public static void main(String[] args) throws IOException {DatagramSocket ds = new DatagramSocket(80);byte[] bytes = new byte[1024];DatagramPacket dp = new DatagramPacket(bytes, bytes.length);while (true) {ds.receive(dp);byte[] data = dp.getData();int len = dp.getLength();String str = new String(data, 0, len, "UTF-8");System.out.println("由" + dp.getAddress().getHostAddress() + "主机" + dp.getPort() +"端口号发来了:");System.out.println(str);if (str.equals("bye")) {break;}}ds.close();}
}

效果如图: 

本次分享到此结束,觉得有所帮助的朋友点点关注点点赞! 

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

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

相关文章

FATE Flow 源码解析 - 日志输出机制

背景介绍 在 之前的文章 中介绍了 FATE 的作业处理流程,在实际的使用过程中,为了查找执行中的异常,需要借助运行生成的日志,但是 FATE-Flow 包含的流程比较复杂,对应的日志也很多,而且分散在不同的文件中&…

Go语言中的并发

简单介绍go中的并发编程. 涉及内容主要为goroutine, goroutine间的通信(主要是channel), 并发控制(等待、退出). 想查看更多与Go相关的内容, 可以查看我的Go编程栏目 Goroutine 语法 在一个函数调用前加上go即可, go func(). 语法很简单, 可以说是并发写起来最简单的程序语言…

【简单介绍Gitea】

🎥博主:程序员不想YY啊 💫CSDN优质创作者,CSDN实力新星,CSDN博客专家 🤗点赞🎈收藏⭐再看💫养成习惯 ✨希望本文对您有所裨益,如有不足之处,欢迎在评论区提出…

广联达Linkworks ArchiveWebService XML实体注入漏洞复现

0x01 产品简介 广联达 LinkWorks(也称为 GlinkLink 或 GTP-LinkWorks)是广联达公司(Glodon)开发的一种BIM(建筑信息模型)协同平台。广联达是中国领先的数字建造技术提供商之一,专注于为建筑、工程和建筑设计行业提供数字化解决方案。 0x02 漏洞概述 广联达 LinkWorks…

【C#】已知有三个坐标点:P0、P1、P2,当满足P3和P4连成的一条直线 与 P0和P1连成一条直线平行且长度一致,该如何计算P3、P4?

问题描述 已知有三个坐标点:P0、P1、P2,当满足P3和P4连成的一条直线 与 P0和P1连成一条直线平行且长度一致,该如何计算P3、P4? 解决办法 思路一:斜率及点斜式方程 # 示例坐标 x0, y0 1, 1 # P0坐标 x1, y1 4, 4 # …

leetcode力扣_二分查找

69.x的平方根 给你一个非负整数 x ,计算并返回 x 的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。 示例 1&…

数据结构——线性表(循环链表)

一、循环链表定义 将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一 个环,这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked list)。 循环链表解决了一个很麻烦的问题。如何从当中一 个结点出发&am…

C# 智慧大棚nmodbus4

窗体 :图表(chart): 下载第三方: nmodbus4:可以实现串口直连,需要创建串口对象设置串口参数配置Serialport 如果需要把串口数据表通过tcp进行网口传递 需要创建tcpclient对象 ModbusSerialMaster master; /…

爬虫(二)——爬虫的伪装

前言 本文是爬虫系列的第二篇文章,主要讲解关于爬虫的简单伪装,以及如何爬取B站的视频。建议先看完上一篇文章,再来看这一篇文章。要注意的是,本文介绍的方法只能爬取免费视频,会员视频是无法爬取的哦。 爬虫的伪装 …

【Arduino IDE】安装及开发环境、ESP32库

一、Arduino IDE下载 二、Arduino IDE安装 三、ESP32库 四、Arduino-ESP32库配置 五、新建ESP32-S3N15R8工程文件 乐鑫官网 Arduino官方下载地址 Arduino官方社区 Arduino中文社区 一、Arduino IDE下载 ESP-IDF、MicroPython和Arduino是三种不同的开发框架,各自适…

基于Web的特产美食销售系统的设计与实现

💗博主介绍💗:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示:文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

SpringBoot框架学习笔记(二):容器功能相关注解详解

1 Spring 注入组件的注解 Component、Controller、 Service、Repository这些在 Spring 中的传统注解仍然有效,通过这些注解可以给容器注入组件 2 Configuration 2.1 应用实例 需求说明: 演示在 SpringBoot, 如何通过Configuration 创建配置类来注入组件 回顾…

客户端与服务器通讯详解(3):如何选择合适的通讯方式

上篇文章中,我们讲解了客户端与服务器通讯详解(2):12种常见通讯方式,重点讲解了http、websocket和RESTful API三种,本文我们继续讲解如何依据场景选择最合适的通讯方式。欢迎友友们点赞评论。 一、客户端服…

微软研究人员为电子表格应用开发了专用人工智能LLM

微软的 Copilot 生成式人工智能助手现已成为该公司许多软件应用程序的一部分。其中包括 Excel 电子表格应用程序,用户可以在其中输入文本提示来帮助处理某些选项。微软的一组研究人员一直在研究一种新的人工智能大型语言模型,这种模型是专门为 Excel、Go…

PDF文件无法编辑?3步快速移除PDF编辑限制

正常来说,我们通过编辑器打开pdf文件后,就可以进行编辑了。如果遇到了打开pdf却不能编辑的情况,那有可能是因为密码或是扫描件的原因。小编整理了一些pdf文件无法编辑,以及pdf文件无法编辑时我们要如何处理的方法。下面就随小编一起来…

JDK新特性(Lambda表达式,Stream流)

Lambda表达式: Lambda 表达式背后的思想是函数式编程(Functional Programming)思想。在传统的面向对象编程中,程序主要由对象和对象之间的交互(方法调用)构成;而在函数式编程中,重点…

Vscode中Github copilot插件无法使用(出现感叹号)解决方案

1、击扩展或ctrl shift x ​​​​​​​ 2、搜索查询或翻找到Github compilot 3、点击插件并再左侧点击登录github 点击Sign up for a ... 4、跳转至github登录页,输入令牌完成登陆后返回VScode 5、插件可以正常使用

Android Framework学习笔记(4)----Zygote进程

Zygote的启动流程 Init进程启动后,会加载并执行init.rc文件。该.rc文件中,就包含启动Zygote进程的Action。详见“RC文件解析”章节。 根据Zygote对应的RC文件,可知Zygote进程是由/system/bin/app_process程序来创建的。 app_process大致处…

好用的AI搜索引擎

1. 360AI 搜索 访问 360AI 搜索: https://www.huntagi.com/sites/1706642948656.html 360AI 搜索介绍: 360AI 搜索,新一代智能答案引擎,值得信赖的智能搜索伙伴,为复杂搜索提供专业支持,解锁更相关、更全面的答案。AI…

pyspark使用 graphframes创建图的方法

1、安装graphframes的步骤 1.1 查看 spark 和 scala版本 在终端输入: spark-shell --version 查看spark 和scala版本 1.2 在maven库中下载对应版本的graphframes https://mvnrepository.com/artifact/graphframes/graphframes 我这里需要的是spark 2.4 scala 2.…