Java 网络编程

文章目录

  • UDP Socket API
    • DatagramSocket
    • DatagramPacket
    • 例子:UDP版本的回显服务器-客户端
  • TCP Socket API
    • ServerSocket
    • Socket
    • 例子:TCP版本的回显服务器-客户端

UDP Socket API

DatagramSocket

这是一个 socket 类,本质上相当于一个文件,在系统中,还有一种特殊的 socket 文件,对应到网卡设备。

构造一个 DatagramSocket 对象,就相当于打开了一个内核中的 socket 文件

构造方法:

构造方法说明
DatagramSocket()构造一个数据报套接字,并将其绑定到本地主机上的任何可用端口
DatagramSocket(int port)构造一个数据报套接字,并将其绑定到本地主机上的指定端口

普通方法:

方法说明
void receive(DatagramPacket p)从该套接字接收数据报,此方法会一直阻塞,直到接收到数据报为止
void send(DatagramPacket p)从此套接字发送数据报
void close()关闭此数据报套接字

DatagramPacket

表示一个 UDP 数据报,UDP 是面向数据报的协议,传输数据就是以 DatagramPacket 为基本单位

构造方法说明
DatagramPacket(byte buf[], int length)构造一个 DatagramPacket,用于接收长度为 length 的数据包,length 参数必须小于或等于 buf.length
DatagramPacket(byte buf[], int length, SocketAddress address)构造一个数据报,用于将长度为 length 的数据报发送到指定主机上的指定端口号。length 参数必须小于或等于 buf.length
DatagramPacket(byte buf[], int offset, int length, SocketAddress address)构造一个数据报,用于将偏移量为 ioffsetlength 长度的数据报发送到指定主机上的指定端口号。length 参数必须小于或等于 buf.length
DatagramPacket(byte buf[], int length, InetAddress address, int port)构造一个数据报,用于将长度为 length 的数据包发送到指定主机上的指定端口号。length 参数必须小于或等于 buf.length
方法说明
InetAddress getAddress()返回发送此数据报或接收数据报的机器的 IP 地址
SocketAddress getSocketAddress()获取此数据包发送到或来自的远程主机的 SocketAddress(通常为IP地址+端口号)
int getPort()返回发送的数据报中的接收端主机端口号,或者接收的数据报中的发送端主机端口号
byte[] getData()返回数据缓冲区
int getLength()返回要发送的数据长度或接收的数据长度

InetSocketAddress

创建 DatagramPacket 时,需要 SocketAddress,该对象通过 InetSocketAddress 创建。

构造方法说明
InetSocketAddress(InetAddress addr, int port)根据 IP 地址和端口号创建套接字地址

例子:UDP版本的回显服务器-客户端

服务器:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UDPEchoServer {private final DatagramSocket socket;public UDPEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器 启动!");while (true) {// 读取客户端发来的请求DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096); // 空的,用于接收响应socket.receive(requestPacket);// 对请求进行解析,把 DatagramPacket 转成 StringString request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 处理响应String response = process(request);// 把响应构造成 DatagramPacket 对象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);}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UDPEchoServer server = new UDPEchoServer(8000);server.start();}
}

客户端:

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UDPEchoClient {private final DatagramSocket socket;public UDPEchoClient() throws SocketException {// 客户端端口一般自动分配socket = new DatagramSocket();}public void start() throws IOException {Scanner scanner = new Scanner(System.in);while (true) {// 客户端从控制台读取数据System.out.print("> ");String request = scanner.next();// 构造 DatagramPacketDatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName("127.0.0.1"), 8000);// 发送给服务器socket.send(requestPacket);// 从服务器读取响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);// 把响应数据转成字符串String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.printf("req: %s; resp: %s\n", request, response);}}public static void main(String[] args) throws IOException {UDPEchoClient client = new UDPEchoClient();client.start();}
}

因为这里的 socket 创建出来就会一直用,所以是伴随程序的整个生命周期的,所以不需要手动调用 close() 去关闭

技巧:Windows 使用 netstat -ano | findstr "端口号",可以查找占用该端口的进程pid

TCP Socket API

ServerSocket

ServerSocket 是创建 TCP 服务端 Socket 的 API

构造方法说明
ServerSocket(int port)创建绑定到指定端口的服务器套接字
方法说明
Socket accept()侦听要与此套接字建立的连接并接受该连接。该方法将阻塞,直到建立连接为止。
void close()关闭此套接字,相当于发送 FIN

Socket

构造方法说明
Socket(String host, int port)创建流套接字并将其连接到命名主机上的指定端口号
方法说明
InetAddress getInetAddress()返回套接字所连接的地址
int getPort()返回此套接字所连接的远程端口号
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

例子:TCP版本的回显服务器-客户端

服务端:

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;public class TCPEchoServer {private final ServerSocket serverSocket;public TCPEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器 启动!");while (true) {Socket clientSocket = serverSocket.accept();// 创建新线程去完成工作,主线程继续acceptThread t = new Thread(() -> {try {processConnect(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});t.start();}}// 短连接:一个连接只进行一次数据交互(一个请求 + 一个响应)// 长连接:一个连接进行多次数据交互(N 个请求 + N 个响应)public void processConnect(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 建立连接\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {Scanner scanner = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);// 长连接while (true) {if (!scanner.hasNext()) {System.out.printf("[%s:%d] 断开连接\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());break;}// 读取请求并解析String request = scanner.next();// 根据请求计算响应String response = process(request);// 把响应写回客户端printWriter.println(response); // 注意补上空白符,如换行,对面next读的时候要读到空白符才往下走printWriter.flush();System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(),clientSocket.getPort(), request, response);}} finally {// 一定要记得关闭 clientSocket// 因为它是 accept 创建出来的,每来一个连接就会创建一个,占用文件描述符资源clientSocket.close();}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TCPEchoServer server = new TCPEchoServer(8000);server.start();}
}

客户端:

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;public class TCPEchoClient {private final Socket socket;public TCPEchoClient() throws IOException {socket = new Socket("127.0.0.1", 8000); // 此时触发三次握手}public void start() throws IOException {Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {Scanner scannerNet = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {// 从控制台读取用户输入System.out.print("> ");String request = scanner.next();// 把请求发送给服务器printWriter.println(request); // 注意补上空白符,如换行,对面next读的时候要读到空白符才往下走printWriter.flush();// 从服务器读取响应String response = scannerNet.next();System.out.printf("req: %s; resp: %s\n", request, response);}}}public static void main(String[] args) throws IOException {TCPEchoClient client = new TCPEchoClient();client.start();}
}

上述服务端代码还可以使用线程池改进:

public void start() throws IOException {System.out.println("服务器 启动!");// 使用线程池,适合写自动扩容版本的ExecutorService service = Executors.newCachedThreadPool();while (true) {Socket clientSocket = serverSocket.accept();service.submit(() -> {try {processConnect(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});}
}

注意

这里的 TCP 服务器之所以使用多线程,是因为处理的是长连接,与客户端建立好连接之后,什么时候断开连接不确定,这一个连接里要处理多少请求,也不确定,单线程处理连接里的循环的时候,就无法 accept 新的连接了。

如果是短连接,每次连接只处理一个请求,就可以不使用多线程了。

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

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

相关文章

C3 多媒体查询

文章目录 前言CSS3 多媒体查询CSS2 多媒体类型CSS3 多媒体查询浏览器支持多媒体查询语法CSS3 多媒体类型多媒体查询简单实例 媒体类型媒体功能更多实例后言 前言 hello world欢迎来到前端的新世界 😜当前文章系列专栏:CSS 🐱‍👓博…

SpringBoot 2 系列停止维护,Java8 党何去何从?

SpringBoot 2.x 版本正式停止更新维护,官方将不再提供对 JDK8 版本的支持 SpringBoot Logo 版本的新特性 3.2 版本正式发布,亮点包括: 支持 JDK17、JDK21 版本 对虚拟线程的完整支持 JVM Checkpoint Restore(Project CRaC&…

Django之Auth模块

Auth模块引入 我们在创建一个Django项目之后,直接执行数据库迁移命令会自动生成很多表 django_sessionauth_userDjango在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且必须是管理员用户才能进入 【…

quickapp_快应用_某些css样式不兼容问题

样式问题 引入css样式文件[1] 单位px [2]选择器[3]盒模型[4]样式布局-默认弹性布局且不可取消[5-1]样式切换-类名的动态切换-语法[5-2]样式切换 - 类名的动态切换-目标元素[5-3] 样式切换 - 行内样式动态切换[6]background[7]overflow[8]border-radius[9]盒子阴影[10] 定位erro…

【调度算法】开放车间调度问题遗传算法(failed)

省流:这是一个错误的代码备份,如果你需要可以直接运行的完整代码,请移步GitHub。 本以为手搓了一个单机调度和并行机调度的遗传算法,就可以尝试写开放车间的遗传算法了,结果手搓了两天,开始作业时间和结束…

1、Mysql架构与历史

Mysql逻辑架构 最上层是服务并不是Mysql所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构,比如连接处理,授权认证,安全等。 第二层是Mysql比较有意思的部分。大多数Mysql的核心服务都在这一层,…

torch.view()和.reshape()

view只能作用在连续的张量上(张量中元素的内存地址是连续的)。而reshape连续or非连续都可以。 调用x.reshape的时候,如果x在内存中是连续的,那么x.reshape会返回一个view(原地修改,此时内存地址不变…

用户与组管理:如何在服务器系统中管理用户和权限

你是否想过,当你登录到一个服务器系统时,你是如何被识别和授权的?你是否知道,你可以通过创建和管理用户和组来简化和优化你的系统管理工作?你是否想了解一些常用的用户和组管理命令和技巧?如果你的答案是肯…

c语言实现简单的string

文章目录 前言一、注意事项二、代码valgrind扫描总结 前言 在c语言中利用面向对象的编程方式,实现类似c中的string类。 一、注意事项 所有与string结构体相关的函数全都没有返回值。 在c中,当产生临时对象时编译器会自动的加入析构函数,销毁…

Walrus 入门教程:如何创建模板以沉淀可复用的团队最佳实践

模板是 Walrus 的核心功能之一,模板创建完成后用户可以重复使用,并在使用过程中逐渐沉淀研发和运维团队的最佳实践,进一步简化服务及资源的部署。用户可以使用 HCL 语言自定义创建模板,也可以一键复用 Terraform 社区中上万个成熟…

云原生安全工具汇总(docker、k8s、Kubernetes、Git仓库)

目录 Metarget:云原生靶机环境 CDK:容器环境定制的渗透测试工具 container-escape-check:容器逃逸检测

synchronized在代码中的用法

synchronized可以对两种对象加锁:实例对象和类对象。下边先说对类对象加锁的代码: 第1是修饰static方法,第2种是直接锁类的class对象; /*** title: SynchronizedStaticDemo1* description: synchronized 对类加锁1* author: * d…

【Python百宝箱】Python数据探险:Excel与数据科学的完美结合

前言 在当今信息爆炸的时代,数据处理和分析已经成为各行各业不可或缺的一部分。在众多数据处理工具中,Python以其简洁而强大的语法成为数据科学家和分析师的首选之一。本文将深入探讨与电子表格处理相关的Python库,介绍它们的功能、应用场景…

批量按顺序1、2、3...重命名所有文件夹里的文件

最新: 最快方法:先用这个教程http://文件重命名1,2......nhttps://jingyan.baidu.com/article/495ba841281b7079b20ede2c.html再用这个教程去空格:利用批处理去掉文件名中的空格-百度经验 (baidu.com) 以下为原回答 注意文件名有空格会失败…

LeetCode Hot100 105.从前序与中序遍历序列构造二叉树

题目&#xff1a;给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 代码&#xff1a; class Solution {private Map<Integer, Integer> indexM…

ESP32网络开发实例-远程Web串口监视器

远程Web串口监视器 文章目录 远程Web串口监视器1、应用介绍2、软件准备3、硬件准备4、代码实现在本文中,我们将构建一个 ESP32 网络服务器,用作远程串行监视器。 基于 Web 的串行监视器的工作方式与通常用于调试目的的 Arduino IDE 串行监视器的工作方式相同。 1、应用介绍 …

数字逻辑电路基础-时序逻辑电路之移位寄存器

文章目录 一、移位寄存器定义二、verilog源码三、仿真结果一、移位寄存器定义 移位寄存器定义 A shift register is a type of digital circuit using a cascade of flip flops where the output of one flip-flop is connected to the input of the next. 移位寄存器是一种将…

docker部署MySQL5.7设置密码和远程访问的方法

运行MySQL docker run -p 3306:3306 --name mysql57 -v /root/mysql/log:/var/log/mysql -v /root/mysql/data:/var/lib/mysql -v /root/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORDD7txumqc2b! -d mysql:5.7 --character-set-serverutf8mb4 --collation-serverutf8…

c++八股文记录

八股文 1.类和结构体的区别 在 C 中&#xff0c;类&#xff08;class&#xff09;和结构体&#xff08;struct&#xff09;在语法上几乎是相同的&#xff0c;唯一的区别是默认的访问权限。在结构体中&#xff0c;默认的访问权限是公有的&#xff08;public&#xff09;&#x…

基于51单片机超市快递寄存自动柜设计源程序

一、系统方案 1、本设计采用这51单片机作为主控器。 2、存包&#xff0c;GSM短信取件码。 3、液晶1620显示。 4、矩阵键盘输入取件码&#xff0c;完成取包。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 /******************************…