Java网络编程API 1
Java中的网络编程API一共有两套:一套是UDP协议使用的API;另一套是TCP协议使用的API。这篇文章我们先来介绍UDP版本的API,并尝试来写一个回显服务器(接收到的请求是什么,返回的响应就是什么)。
UDP数据报套接字编程
API介绍
DatagramSocket
DatagramSocket是UDPSocket,用于发送和接收UDP数据报,就相当于网卡的作用。
DatagramSocket的构造方法:
DaragramSocket方法:
DatagramPacket
DatagramPacket是UDPSocket发送和接收的数据报。
DatagramPacket构造方法:
DatagramPacket方法:
构造UDP发送数据报的时候,需要传入SocketAddress(要发送信息的地址)。
补充:
UDPSocket中API的使用,是Java把操作系统中的原生API进行了一层封装。
其中最核心的类就是上面两个,DatagramSocket和DatagramPacket。
DatagramSocket:操作系统中有一类文件,就叫做socket(文件普通文件和目录文件都是存放在硬盘上的,socket文件,就抽象地表示了“网卡”这样的硬件设备,进行网络通信最核心的硬件设备就是网卡(通过网卡发送数据,就相当于写socket文件;通过网卡接收数据,就相当于读socket文件)。
DatagramPacket:UDP面向的是数据报,每次发送、接收数据的基本单位,就是一个UDP数据报,而DatagramPacket就表示了一份UDP数据报。
下面我们通过写一个回显服务器,来认识这些API的使用,并且了解在实际开发中,服务器需要做哪些事情,客户端需要做哪些事情。
回显服务器
回显服务器:客户端发什么请求,服务器就返回什么响应。(并没有什么业务逻辑,实际开发中,我们要返回什么响应是要根据业务逻辑来的)。
服务器端
第一步,先创建DatagramSocket对象,并且通过构造方法为服务器指定一个端口号:
我们的服务器程序一启动,就需要绑定/关联上操作系统的一个端口号,端口号也是一个整数,用来区分一个主机上进行网络通信的程序(一个端口号只能绑定一个程序,反过来,一个程序能够绑定多个端口号,上面抛出的SocketException就是为了处理多个程序绑定了同一个端口号导致Socket创建不成功的情况),在创建服务器时需要我们手动指定一个端口号。
下面我们来写start方法:
对于服务器来说,需要不停地接收请求,返回响应,接收请求,返回响应。(一个服务器单位时间能处理地请求,能返回地响应越多,服务器水平越高)。
服务器往往都是7*24小时运行,因此这里地while(true)并没有退出的必要,如果我们确实向重启服务器,直接“杀”进程即可。
当然,我们这里的while(true)是非常简单粗暴的写法。实际开发中的服务器,很可能要实现“优雅退出”的效果,即确保当前正在进行的请求做完了之后再进行退出。
在start方法中,我们要完成四步工作;
1、读取请求并解析。
2、根据请求计算响应(但我们这里只是简单构造回显服务器,这一步啥也不用干)。
3、把响应返回给客户端。
4、打印日志。
1、读取请求并解析
读取请求并解析的过程中,需要使用socket的receive方法:
使用receive需要传入一个输出型参数DatagramPacket对象,这个对象里有一个内置的字节数组(我们可以设置数组的最大长度),会保存我们收到的消息正文(应用层数据包,UDP数据报的载荷部分)。此外UDP报头、数据的源IP和源端口号等都会被这个DatagramPacket对象所保存。 注意:这里的receive方法是自带阻塞功能的,如果客户端没有发来请求就会阻塞等待。
我们可以将得到的字节数组,转换成String,方便后续根据请求计算响应:
基于字节数组构造出String,字节数组里面保存的内容不一定是二进制数据,也可能是文本数据,如果是文本数据,将其交给String ,也是没有问题的;如果是二进制数据,String也是可以保存的。
第一个参数中,通过requestPacket对象调用getData方法,将上面字节数组中的信息获取到,然后第二个参数表示从字节数组的0号位置,开始构造String,第三个参数,是通过requestPacket对象获取到字节数组的有效长度(如果通过最大长度构造,那么字符串后面的很大部分都会是空白)。通过这三个参数来构造我们的String对象。
补充:
receive是传输层的UDP协议提供的一个API,传输层会给每个socket对象在内核中分配一个缓冲区,每次网卡读到一个数据读到一个数据,都会层层分用,解析好之后,最后放到这个缓冲区中,应用程序调用receive本质上就是从缓冲区中拿走一个数据。
2、根据请求计算响应
因为是回显服务器,这一步我们就只需要简单return以下即可
3、把响应返回到客户端
要想把响应返回给客户端,这里需要使用send方法,这里面同样需要传一个DatagramPacket对象:
此时我们构造DatagramPacket时,参数又不一样了:
第一个参数:response.getByte()是将我们刚才得到的响应(String类型)以字节数组的形式得到。
第二个参数:response.getBytes().length这里是获取字节数组的长度,以字节为单位进行计算,如果传入的参数是response.length(),此时的单位就是字符了。
第三个参数:requestPacket.getSocketAddress(),这里调用方法的的对象是requestPacket,这个对象是我们用来读取请求的,表示的是客户端来的数据报。调用getSocketAddress方法,调用这个方法,会获取到一个InetAddress对象,这个INetAddress对象,就包含了和服务器通信对应的客户端的ip和端口号(即响应的目的ip和目的端口号)。
通过上面的代码,我们也可以看出UDP是无连接的通信。UDP的DatagramSocket对象自身不保存对端的IP和端口。而是在每一份数据报中,都有一个对端的IP和端口。另外代码中也没有建立连接和接收连接的操作。
而UDP的不可靠传输,无法在代码中体现。但是UDP面向数据报的特点,可以看到,在send返回响应和receive读取请求时,都是以DatagramPacket为单位的。
4、打印日志
注意:这里的requestPacket.getAddress().toString会以点分十进制的方式输出客户端的IP地址
再写一个main方法:
注意:这里传入的端口号一般在1024~65535之间(但不可以和其他进程冲突),1~1024之前的端口号是系统保留自用的端口号——知名端口号(不可占用)。
完整代码:
package UDPEcho;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;public class UDPEchoSever {//先创建一个DatagramSocket对象private DatagramSocket socket = null;//服务启动需要绑定端口号//一个主机的一个端口只能绑定一个进程,反过来一个进程可以绑定多个端口public UDPEchoSever(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");//需要不停接收请求并返回响应while(true){//1、读取请求并解析//通过这个字节数组保存收到的消息正文(应用层数据包),也就是UDP数据报的载荷部分DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//此处的receive就从网卡读取到了一个UDP数据报,放到requestPacket中,载荷部分就被放到requestPacket内置的字节数组中了//此处的UDP报头、源ip和端口号也会被保存socket.receive(requestPacket);//把读到的字节数组转为字符串String request = new String(requestPacket.getData(),0, requestPacket.getLength());//2、根据请求构造响应String response = process(request);//3、把响应返回给客户端//构造体格DatagramPacket作为响应对象,里面不是空白的字节数组了,而是把String里面包含的字节数组拿进来了//注意:获取长度是字节数组,单位是字节。如果直接获取字符串长度,单位是字符不一样的//requestPacket.getSocketAddress()这个对象就包含了你和服务器对应的ip和端口号DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//打印日志System.out.printf("[%s:%d],res:%s,response:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UDPEchoSever udpEchoSever = new UDPEchoSever(9090);udpEchoSever.start();}
}
客户端
编写客户端的代码,也需要创建一个DatagramSocket对象。但是此处就不必指定端口号了。
服务器编写代码的时候需要手动指定端口号,但是在客户端这边,一般不需要手动指定,系统会自动分配一个空闲的端口号。
代码中手动指定端口号,可以保证端口号始终都是固定的,如果不手动指定,依赖系统自动分配,导致服务器每次重启之后,端口号可能就变了,一旦变了,客户端就有可能找不到这个服务器在哪里了,所以我们的服务器代码,一般要手动指定端口号。
但是客户端,端口号让系统随机分配一个空闲的即可。(主要是我们无法确保手动指定的端口号是可用的(可能被其他进程占用了))。
服务器的端口就不会被占用吗?为什么只担心客户端呢?
服务器的机器是在程序员手里的,是可控的。程序员事先编写代码之前,是能知道服务器上有哪些端口是空闲的。但是,客户端时在普通用户的机器上的,用户千千万万,上面的环境也是千差万别,天知道某个用户会装什么奇奇怪怪的程序把端口号占用,此时,我们的程序无法正常启动,用户只会怪到程序员的头上。
上面的客户端构造方法还需要,我们需要指定服务器短的ip地址和端口号。因为时客户端主动给服务器发起请求,发起请求的前提就是需要知道服务器在哪里~~~(比如说我要去餐厅吃饭,我就需要知道餐厅的位置在哪里,才能去吃饭)
此处,客户端发起请求的目的ip(severIp)和目的端口(severPort)就是客户端的ip和端口,而请求的源ip就是客户端本机的ip,源端口就是客户端本机分配的端口。
接下来,和服务器一样我们的客户端也需要实现一个start方法,在start方法中,我们也要做四件事:
1、从控制台读取要发送的请求数据
2、构造请求并发送
3、读取服务器的响应
4、把响应显示在控制台上
因为我们是要从控制台读取要发送的数据,所以需要先创建一个Scanner对象,还需要一个循环不断读取数据并发送给服务器。
1、从控制台读取要发送的请求数据
这里的if语句是可以让用户在结束输入的时候,程序可以正确地跳出循环,避免程序一直等待用户输入。
从控制台读取数据的时候,最好使用scanner读取字符串,最好使用next而不是nextLine(如果是文件的话就无所谓了)
如果使用nextLine读取,就需要手动输入换行符——Enter来控制。而由于Enter键不仅会产生\n这样的换行,还会产生其他字符,就可能会导致读到的内容出现问题。
而next是以"空白符"作为分隔符,包括但不限于,换行,空格,制表符……
2、构造请求并发送
同样的socket调用send方法,同样需要传入一个DatagramPacket类型的参数。
此处的Datagram参数又有所不同,第三个参数是我们当前要通信服务器的ip(注意:这里并不能传入String类型的severIP,而是需要调用InetAddress.getByName(severIP)),第四个参数则是我们当前要通信服务器上的端口号。
3、读取服务器的响应
这里仍然是使用socket的receive方法同样参数需要传入一个DatagramPacket类型的对象,此时构造方法仍然搭配receive使用,所以构造方法只需要指空白的字节数组即可
4、把响应显示在控制台上 
这里也需要基于DatagramPacket构造字符串,进行打印。
主函数:
其中127.0.0.1表示的是本机的ip,9090就是我们刚才在服务器中指定的端口号
完整代码:
package UDPEcho;import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;public class UDPEchoCline {private DatagramSocket socket = null;//客户端的IPprivate String severIp;//客户端端口号private int severPort;public UDPEchoCline(String severIp,int severPort) throws SocketException {this.severIp = severIp;this.severPort = severPort;socket = new DatagramSocket();}public void start() throws IOException {System.out.println("客户端启动");Scanner scanner = new Scanner(System.in);while(true) {System.out.print("->");//1、从控制台读取要发送请求的数据if(!scanner.hasNext()){break;}String request = scanner.next();//2、构造请求并发送DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(severIp),severPort);socket.send(requestPacket);//3、读取服务器响应DatagramPacket responesePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responesePacket);//4、把响应打印到控制台上String response = new String(responesePacket.getData(),0,responesePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UDPEchoCline udpEchoCline = new UDPEchoCline("127.0.0.1",9090);udpEchoCline.start();}
}
基于DatagramPacket的补充
第一种:构造时指定空白的字节数组和字节数组的长度即可(搭配receive进行使用):
第二种:构造的时候指定有内容的字节数组,并且通过requestPacket.getSocketAddress方法获取请求方的IP和端口号。(用于服务器向客户端返回响应)。
第三种:构造的时候指定有内容的字节数组,并且分别将数据的目的IP和端口号作为两个参数。(用于客户端向服务器发起请求)
代码演示:
注意:这里一定要先启动服务器,再启动客户端
此时,我们再客户端控制台输入,就会返回同样的值
同时,我们的服务器也会打印日志
梳理代码
下面是一个大致的流程图:
图文并茂讲解:
1、服务器启动之后,进入receive阻塞,等待客户端的请求。
2、客户端在启动之后,在hasnext进入阻塞,等待用户输入。 3、用户在控制台输入之后,客户端会拿到用户输入的字符串,构造出请求,发送请求,并且在receive处等待响应返回。
4、服务器收到响应,就从receive解除阻塞,继续往下执行。
这里服务器执行完上述逻辑后,就会进入到下次循环中的receive的阻塞等待中,等待下一个客户端的请求过来。
5、客户端receive中返回,得到了服务器返回的响应数据,并且将数据打印在控制台上。
客户端在打印完毕之后也会进行下一次循环,等待用户再次从控制台输入信息。
补充 :
我们可以把写好的服务器代码,放到一个云服务器上,从而实现跨主机通信。
相关文章:

Java网络编程API 1
Java中的网络编程API一共有两套:一套是UDP协议使用的API;另一套是TCP协议使用的API。这篇文章我们先来介绍UDP版本的API,并尝试来写一个回显服务器(接收到的请求是什么,返回的响应就是什么)。 UDP数据报套…...
Android协程学习
目录 Android上的Kotlin协程介绍基本概念与简单使用示例协程的高级用法 结构化并发线程调度器(Dispatchers)自定义调度器并发:同步 vs 异步 异步并发(async 并行执行)同步顺序执行协程取消与超时 取消机制超时控制异步数据流 Flow协程间通信 使用 Channel使用 StateFlow /…...
Angular报错:cann‘t bind to ngClass since it is‘t a known property of div
遇到的错误: Cant bind to ngClass since it isnt a known property of div这个错误是 Angular 中 最常见的模板编译错误之一,通常出现在你试图使用 ngClass 指令,但 Angular 没有识别它的情况下。 ✅ 错误的根本原因 Angular 不知道 ngCla…...
uniapp+vue3实现CK通信协议(基于jjc-tcpTools)
1. TCP 服务封装 (tcpService.js) export class TcpService {constructor() {this.connections uni.requireNativePlugin(jjc-tcpTools)this.clients new Map() // 存储客户端连接this.servers new Map() // 存储服务端实例}// 创建 TCP 服务端 (字符串模式)createStringSe…...
Python爬虫实战:研究urlparse库相关技术
1 引言 1.1 研究背景与意义 网络爬虫作为互联网数据采集的核心技术,在信息检索、舆情分析、数据挖掘等领域具有广泛应用。随着 Web 技术的发展,现代网站 URL 结构日益复杂,包含路径参数、查询参数、锚点等多种组件,且存在相对路径、URL 编码等问题,给爬虫开发带来了挑战…...
解锁FastAPI与MongoDB聚合管道的性能奥秘
title: 解锁FastAPI与MongoDB聚合管道的性能奥秘 date: 2025/05/20 20:24:47 updated: 2025/05/20 20:24:47 author: cmdragon excerpt: MongoDB聚合管道是一种分阶段处理数据的流水线,通过$match、$group等阶段对文档进行特定操作,具有内存优化和原生操…...
软件工程方法论:在确定性与不确定性的永恒之舞中寻找平衡
更多精彩请访问:通义灵码2.5——基于编程智能体开发Wiki多功能搜索引擎-CSDN博客 当我们谈论“软件工程”时,“工程”二字总暗示着某种如桥梁建造般的精确与可控。然而,软件的本质却根植于人类思维的复杂性与需求的流变之中。软件工程方法论的…...
Unity中的MonoSingleton<T>与Singleton<T>
1.MonoSingleton 代码部分 using UnityEngine;/// <summary> /// MonoBehaviour单例基类 /// 需要挂载到GameObject上使用 /// </summary> public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T> {private static T _instance;…...
怎么通过 jvmti 去 hook java 层函数
使用 JVMTI 手动实现 Android Java 函数 Hook 要通过 JVMTI 手动实现 Android Java 函数 Hook,需要编写 Native 层代码并注入到目标进程中。以下是详细步骤和示例: 一、核心实现原理 JVMTI 提供两种主要 Hook 方式: Method Entry/Exit 事…...

兰亭妙微 | 医疗软件的界面设计能有多专业?
从医疗影像系统到手术机器人控制界面,从便携式病原体检测设备到多平台协同操作系统,兰亭妙微为众多医疗设备研发企业,打造了兼具专业性与可用性的交互界面方案。 我们不仅做设计,更深入理解医疗场景的实际需求: 对精…...

前端原生构建交互式进度步骤组件(Progress Steps)
在现代网页设计中,进度步骤(Progress Steps) 是一种常见的 UI 模式,常用于引导用户完成注册流程、多步表单、教程或任何需要分步骤操作的场景。本文将带你从零开始构建一个美观且功能完整的 “进度步骤”组件,并详细讲…...
如何给windos11 扩大C盘容量
动不动C盘就慢了,苹果逼着用户换手机,三天两头更新系统,微软也是毫不手软。c盘 从10个G就够用,到100G 也不够,看来通货膨胀是部分行业的。 在 Windows 11 中扩大 C 盘容量,主要取决于磁盘分区布局和可用空…...

【基于阿里云搭建数据仓库(离线)】Data Studio创建资源与函数
Data Studio支持在您的数据分析代码中引用自定义的资源和函数(支持MaxCompute、EMR、CDH、Flink),您需要先创建或上传资源、函数至目标工作空间,上传后才可在该工作空间的任务中使用。您可参考本文了解如何使用DataWorks可视化方式…...
Linux_T(Sticky Bit)粘滞位详解
Linux 粘滞位(Sticky Bit)详解 一、什么是粘滞位(Sticky Bit) 粘滞位(Sticky Bit)是 Linux 和 Unix 系统中一种特殊的权限设置,主要应用于目录,其作用是在多人共享访问的目录中&am…...

web3-以太坊智能合约基础(理解智能合约Solidity)
以太坊智能合约基础(理解智能合约/Solidity) 无需编程经验,也可以帮助你了解Solidity独特的部分;如果本身就有相应的编程经验如java,python等那么学起来也会非常的轻松 一、Solidity和EVM字节码 实际上以太坊链上储存…...
高敏感应用如何保护自身不被逆向?iOS 安全加固策略与工具组合实战(含 Ipa Guard 等)
如果你正在开发一款涉及支付、隐私数据或企业内部使用的 App,那么你可能比多数开发者更早意识到一件事——App 一旦被破解,损失的不只是代码,还有信任与业务逻辑。 在我们为金融类工具、HR 系统 App、数据同步组件等高敏感项目提供支持的过程…...

【C++项目】负载均衡在线OJ系统-2
文章目录 oj_server模块编写oj_server框架的搭建-oj_server/oj_server.cpp 路由框架 oj_model模块编写题目信息设置v1.文件版本-common/util.hpp boost库spilt函数的使用-oj_server/oj_model_file.hpp 文件版本model编写v2.mysql数据库版本1.mysql创建授权用户、建库建表录入操…...

GC1809:高性能24bit/192kHz音频接收芯片解析
1. 芯片概述 GC1809 是数字音频接收芯片,支持IEC60958、S/PDIF、AES3等协议,集成8选1输入切换、低抖动时钟恢复和24bit DAC,适用于家庭影院、汽车音响等高保真场景。 核心特性 高精度:24bit分辨率,动态范围105dB&…...

2025年06月05日Github流行趋势
项目名称:onlook 项目地址url:https://github.com/onlook-dev/onlook项目语言:TypeScript历史star数:16165今日star数:1757项目维护者:Kitenite, drfarrell, spartan-vutrannguyen, apps/devin-ai-integrat…...
flask功能使用总结和完整示例
Flask 功能使用总结与完整示例 一、Flask 核心功能总结 Flask 是轻量级 Web 框架,核心功能包括: 路由系统:通过 app.route 装饰器定义 URL 与函数的映射。模板引擎:默认使用 Jinja2,支持动态渲染 HTML。请求处理&…...
AWS 亚马逊 S3存储桶直传 前端demo 复制即可使用
自己踩过坑不想别人也踩坑了 亚马逊S3存储桶直传前端demo复制即可使用 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0…...
DAY 15 复习日
浙大疏锦行 数据使用爬虫爬取weibo数据,下面是代码 import datetime import os import csv import timeimport numpy as np import random import re import urllib.parse import requests from fake_useragent import UserAgentdef init():if not os.path.exists…...
Vue Router 导航方法完全指南
📖 前言 在 Vue 项目中,我们经常需要在不同页面之间跳转,或者更新当前页面的 URL 参数。Vue Router 提供了几种不同的导航方法,每种方法都有其特定的使用场景。本文将详细讲解这些方法的区别和最佳实践。 🎯 核心概念…...
MidJourney入门学习
1. 引言 MidJourney 是一款由美国科技公司开发的先进文本到图像生成 AI 工具,自 2022 年推出以来迅速在创意产业和社交媒体领域引发轰动。与 Stable Diffusion 不同,MidJourney 以其独特的美学风格、高度细节化的图像生成能力和强大的创意引导功能著称,成为设计师、艺术家和…...
2025最新Java日志框架深度解析:Log4j 2 vs Logback性能实测+企业级实战案例
一、为什么printStackTrace是"代码坟场"? 你写的日志可能正在拖垮系统! 在Java开发中,直接调用printStackTrace()打印异常堆栈是最常见的"自杀式操作"。这种方式会导致三大致命问题: 无法分级控制ÿ…...
如何安全高效的文件管理?文件管理方法
文件的管理早已不只是办公场景中的需求。日常生活、在线学习以及个人收藏中,文件管理正逐渐成为我们数字生活中的基础。但与此同时,文件管理的混乱、低效以及安全性问题也频繁困扰着许多人。 文件管理的挑战与解决思路 挑战一:文件存储无序…...

基于BI PaaS架构的衡石HENGSHI SENSE平台技术解析:重塑企业级数据分析基座
在数据驱动决策的时代,传统BI工具日益显露出扩展性弱、灵活性差、资源利用率低等痛点。衡石科技推出的HENGSHI SENSE平台,创新性地采用BI PaaS(平台即服务)架构,为企业构建了一个强大、开放、可扩展的数据分析基础设施…...
Hive中ORC存储格式的优化方法
优化Hive中的ORC(Optimized Row Columnar)存储格式可显著提升查询性能、降低存储成本。以下是详细的优化方法,涵盖参数配置、数据组织、写入优化及监控调优等维度: 一、ORC核心参数优化 1. 存储与压缩参数 SET orc.block.size=268435456; -- 块大小(默认256MB)…...
代码训练LeetCode(23)随机访问元素
代码训练(23)LeetCode之随机访问元素 Author: Once Day Date: 2025年6月5日 漫漫长路,才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 380. O(1) 时间插入、删除和获取随机元素 - 力扣(LeetCode)力…...

【R语言编程绘图-plotly】
安装与加载 在R中使用plotly库前需要安装并加载。安装可以通过CRAN进行,使用install.packages()函数。加载库使用library()函数。 install.packages("plotly") library(plotly)测试库文件安装情况 # 安装并加载必要的包 if (!requireNamespace("p…...