当前位置: 首页 > news >正文

网络编程相关 API 学习

目录

1. 网络编程中的基本概念

2. UDP 的 socket api 的使用

(1) DatagramSocket API

(2) DatagramPacket API

(3) InetSocketAddress API

(4) 使用 UDP 的 socket api 

3. TCP 的 socket api 的使用

(1) ServerSocket API

(2) Socket API


1. 网络编程中的基本概念

客户端:主动发起请求的一方。

服务器:被动接受请求的一方。

客户端给服务器发起请求,服务器就会给客户端返回响应。

比如说,我去吃板面,那我一进门店,我就会对着老板说:给我来一份小碗板面(发请求),那老板就会立马去后厨,给我煮板面,等老板做好板面后,她就会给我端上来(返回响应)。

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

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

Socket API 可以认为是应用层和传输层之间交互的路径,通过这一套 Socket API 可以完成不同主机之间,不同系统之间的网络通信。

传输层提供的网络协议主要就是两个:1. TCP   2. UDP

因为这两个协议的特性差异很大,操作起来也不同,所以系统就分别为它们各自提供了一套 API。

那么 TCP 和 UDP 有什么区别呢?

2. UDP 的 socket api 的使用

(1) DatagramSocket API

socket 其实也是操作系统的一个概念,本质上是一种特殊的文件。

Socket 就属于是把 "网卡" 这个设备,给抽象成文件了。

往 socket 文件中写数据,就相当于通过网卡发送数据,

往 socket 文件中读数据,就相当于通过网卡接收数据。

这样就把网络通信和文件操作统一了。

而 Java 中就是通过使用 DatagramSocket,来表示系统内部的 socket 文件了。

DatagramSocket 构造方法:

DatagramSocket 提供了以下方法:

因为 socket 是个文件,所以使用完后就需要关闭,要是一直频繁打开文件,而不去关闭,文件描述符表就可能会被吃满,就有可能导致文件资源泄露。

(2) DatagramPacket API

DatagramPacket 就是 UDP 数据报,UDP 会以数据报为单位发送或者接收数据。

(3) InetSocketAddress API

构造发送的数据报的时候,需要传 SocketAddress 对象,这个对象可以使用 InetSocketAddress 来创建,InetSocketAddress 是 SocketAddress 的子类。

(4) 使用 UDP 的 socket api 

我们可以写一个简单的 UDP 客户端/服务器 通信的程序。

回显服务器:

这个程序没有什么业务逻辑,就是单纯调用 socket api。

让客户端发送一个请求,请求就是一个从控制台输入的字符串。

服务器收到字符串后,也会把这个字符串原封不动还给客户端,客户端再显示出来。

那就直接先创建两个类来表示 UDP 客户端 和 UDP 服务器,然后就是按照刚刚说的方式,分不同的角色,服务器就是先从客户端读取数据报,也就得到了请求字符串,然后再根据请求字符串解析成响应字符串,然后再将响应字符串构造成一个数据报,最后返回给客户端。

而客户端就是先从控制台读取请求字符串,然后将请求字符串构造成数据报发送给服务器,然后从服务器那里获取响应数据报,从而获取到响应字符串,最后将响应字符串打印在控制台上即可。

写完之后是这样的:

public class UdpEchoClient {// 首先创建 UDP socketprivate DatagramSocket socket;// 这里不需要给客户端指定端口,而是让系统自动分配// 防止程序员自己指定的端口号跟用户主机的其他程序产生冲突// 需要传服务器的 ip 和端口,因为 udp 不会记录对端信息public UdpEchoClient(String serverIp, int serverPort) throws IOException {// 创建这个对象,不能手动指定端口socket = new DatagramSocket();Scanner scan = new Scanner(System.in);// 1. 从控制台输入请求字符串// 2. 构造数据报发送给服务器// 3. 从服务器获取响应// 4. 将获取到的响应字符串打印while (true) {// 读取请求System.out.print("->");String requestString = scan.next();// 构造请求数据包并发送DatagramPacket requestPacket = new DatagramPacket(requestString.getBytes(), 0, requestString.getBytes().length,new InetSocketAddress(InetAddress.getByName(serverIp), serverPort));socket.send(requestPacket);// 获取响应数据报DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);String responseString = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.println(responseString);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 8888);}
}
public class UdpEchoServer {// 创建一个 DatagramSocket 对象,后续操作网卡的基础private DatagramSocket socket;// 构造方法, 需要传个端口号来绑定public UdpEchoServer(int port) throws IOException {// 这么写就是手动指定服务器绑定的端口socket = new DatagramSocket(port);// 1. 从客户端那里读取数据报// 2. 处理读到的数据// 3. 并构造一个数据报返回响应while (true) {DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);// 读取请求socket.receive(requestPacket);// 当前完成 receive 之后,数据是以 二进制 的形式存储到 DatagramPacket 中了// 要想把这里的数据给显示出来,还需要把这个二进制数据给转成字符串// 处理请求, 将二进制数据转化成字符串String requestString = new String(requestPacket.getData(), 0, requestPacket.getLength());// 根据请求计算响应(一般的服务器都会经历的过程)// 由于是回显服务器,请求是啥样,响应就是啥样String responseString = process(requestString);// 将响应返回给客户端// 往 DatagramPacket 里构造刚才的数据,再通过 send 返回DatagramPacket responsePacket = new DatagramPacket(responseString.getBytes(), 0, responseString.getBytes().length,requestPacket.getAddress(), requestPacket.getPort());socket.send(responsePacket);// 打印一个日志,把这次数据交互的详情给打印出来System.out.printf("[%s:%d] req=%s,resp=%s\n", requestPacket.getAddress().toString(),requestPacket.getPort(), requestString, responseString);}}private String process(String data) {return new String(data);}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(8888);}
}

运行下程序看看,首先启动服务器,再来启动客户端。

 没问题。

 其中需要注意的地方就是服务器需要绑定指定的端口,而客户端的端口是由系统自动分配,不需要手动绑定。

服务器绑定端口是为了方便用户来找到它,而如果客户端也绑定端口的话,这个端口号就可能会和用户主机上的程序的端口号发送冲突,所以客户端的端口号就交给系统去分配比较好,系统分配的话,就一定会分配一个空闲的端口号。

写 while true 是因为服务器不可能运行一下子就结束了,通常情况下,服务器是要 7*24 小时运行的。

没调用 close 关闭 socket 也不会出现文件资源泄露,刚刚也说过,文件资源泄露的原因是一直频繁打开文件,而不去关闭文件,这里我们的 socket 不需要 close,因为我们没有频繁的打开文件,而且不能把 socket 提前释放掉,因为客户端还需要发送请求,当我们程序结束的时候,进程就会销毁,文件描述符表也会被回收,所以自然不用担心文件资源泄露,客户端的话,使用周期比较短,进程很快就会结束掉。

核心网络编程流程:

1. 读取请求并解析

2. 根据请求计算响应

3. 把响应写回到客户端

接下来我们再来看看 TCP 的 socket api

3. TCP 的 socket api 的使用

ServerSocket 和 Socket 都是用来表示 socket 文件的(抽象了网卡这样的硬件设备)

(1) ServerSocket API

socket 的 api 差异又很大,但是和文件操作,是有密切联系的。

TCP 面向字节流,传输基本单位是 byte .

ServerSocket 是给服务器使用的类,使用这个类来绑定端口号。

前面也提到过,TCP 的特点是有连接,可靠传输,面向字节流,全双工。

那这个连接,就是通信双方会记录对方的信息,

所以使用 TCP 的方式来网络通信的话,通信双方就必须得先建立连接。

建立连接这件事操作系统内核帮我们做了,我们需要做的就是:

客户端发起建立连接的动作,然后让服务器把建立好的连接从内核中拿到应用程序里。 

这个 ServerSocket 就只是用来取连接的。

然后我们再来看看 ServerSocket 的构造方法:

(2) Socket API

socket 既会给服务器用,又会给客户端用。

我们来看看 Socket 的方法:

了解了 TCP 的 socket api 后,我们就可以去写一个回显服务器啦。

还是那个核心逻辑:1. 读取请求  2. 根据请求计算响应  3. 将响应返回给客户端。

但是想要通信,首先得建立连接才行,用 ServerSocket 调用 accept 方法就能拿到连接,然后使用 Socket 来进行与客户端的通信。其实思路跟 UDP 的差不多,服务器的话,就是循环从系统内核的队列中拿连接,拿到连接后就可以通过连接得到 Socket 对象,然后就是通过 Socket 对象完成与客户端的通信,可能有多次请求,那就写个循环,然后还是先获取请求,然后根据请求计算响应,最后将响应返回给客户端(ps: 写文件的时候不要忘记调用 flush 方法冲刷缓冲区)。

然后客户端的话,还是写个循环,先从控制台输入请求,然后将请求发送给服务器,然后从服务器拿到响应,最后将响应打印在控制台上。

写完后是这样的:

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

看起来好像没有问题,但是不要忘记存在多个客户端的情况。

分析原因:

稍微修改下服务器的代码:

这样就没问题了。

完整代码:

public class TcpEchoServer {// 首先创建个 ServerSocket 对象private ServerSocket serverSocket;// 写构造方法,需要指定端口号public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}// 写个 start 方法,通过 start 方法来完成主要逻辑public void start() throws IOException {// 创建线程池,保证 processConnection 和 循环获取连接 能并发执行ExecutorService executor = Executors.newCachedThreadPool();// 用 serverSocket 来取连接,连接可能有多个,用循环while (true) {Socket clientSocket = serverSocket.accept();// 通过 processConnection 来完成服务器与客户端的交互executor.submit(() -> {processConnection(clientSocket);});}}private void processConnection(Socket clientSocket) {System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 1. 获取请求// 2. 根据请求计算响应// 3. 返回响应// 通过流对象来完成,但是要记得使用完后关闭流对象try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {Scanner scan = new Scanner(inputStream);// 首先判断是否有请求if (!scan.hasNext()) {// 没请求的话,说明客户端下线了,那这个连接就关闭了,循环直接跳出即可System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 获取请求, 此处用 \n 来表示一个数据包的结束String request = scan.next();// 根据请求,计算响应String response = process(request);// 将响应返回PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);// 千万不要忘记冲刷缓冲区!!!!printWriter.flush();}} catch (IOException e) {throw new RuntimeException(e);}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(8888);tcpEchoServer.start();}
}
public class TcpEchoClient {// 先创建一个 Socket 对象private Socket socket;// 提供构造方法,传服务器的 ip 以及端口号public TcpEchoClient(String serverIp, int port) throws IOException {// 此时就相当于发送连接请求socket = new Socket(serverIp, port);}// 通过 start 方法来完成主逻辑public void start() {try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();Scanner scan = new Scanner(System.in);Scanner scanRead = new Scanner(inputStream)) {while (true) {System.out.println("->");// 1. 读取输入的请求String request = scan.next();// 2. 将请求发送给服务器PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(request);// 不要忘记冲刷缓冲区!!!printWriter.flush();// 3. 接收响应String response = scanRead.next();// 4. 打印响应System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 8888);tcpEchoClient.start();}
}

相关文章:

网络编程相关 API 学习

目录 1. 网络编程中的基本概念 2. UDP 的 socket api 的使用 (1) DatagramSocket API (2) DatagramPacket API (3) InetSocketAddress API (4) 使用 UDP 的 socket api 3. TCP 的 socket api 的使用 (1) ServerSocket API (2) Socket API 1. 网络编程中的基本概念 客…...

python使用python-docx处理word

文章目录 一、python-docx简介二、基本使用1、新建与保存word2、写入Word(1)打开文档(2)添加标题(3)添加段落(4)添加文字块(5)添加图片(6&#xf…...

【笔记2-1】ESP32:基于vscode的espidf插件的开发环境搭建

主要参考b站宸芯IOT老师的视频,记录自己的笔记,老师讲的主要是linux环境,但配置过程实在太多问题,就直接用windows环境了,老师也有讲一些windows的操作,只要代码会写,操作都还好,开发…...

集成网关 -- 新节点的开发说明

在node-red中,nVisual节点可以帮助我们更快快简捷的完成新的功能,今天我来分享一下关于node-Red中关于nVisual新节点开发基础教程。 首先来看一下基本node-red节点文件目录,当前新开发的7个节点都放在了“node-red\packages\node_modulesno…...

【Gitlab】CICD使用minio作为分布式缓存

1、安装minio 下载适合自己系统版本的安装文件https://dl.min.io/server/minio/release/windows-amd64/ yum install xxx.rpm 2、配置/etc/profile export MINIO_ACCESS_KEYroot [ui登录账号] export MINIO_SECRET_KEYminioDev001 [ui登录密码] export MINIO_OPTS"…...

️️耗时一周,肝了一个超丝滑的卡盒小程序

前言 先看看成品效果: 在上个月,我出于提升自己的英语造句能力的目的,想要找一个阅读或者练习造句类的英语学习 APP,但是最终找了几个 APP 不是不太好用就是要付费。于是我转换思路,找到了一本书,叫《36…...

网络安全审计

网络安全审计是为了确保网络系统的安全性和完整性,防范潜在的网络攻击和数据泄露风险。 审计步骤: 1.确定审计目标:明确审计的目的和范围,例如审计网络设备、服务器、应用程序或数据库等。 2.收集信息:收集审计范围…...

论文:IoU Loss for 2D/3D Object Detection

摘要:在2D/3D目标检测任务中,IoU (Intersection-over- Union)作为一种评价指标,被广泛用于评价不同探测器在测试阶段的性能。然而,在训练阶段,通常采用常见的距离损失(如L1或L2)作为损失函数,以最小化预测值…...

2411mfc,修改按钮颜色

添加消息:ON_WM_CTLCOLOR() //在OnInitDialog()方法中添加{HWND hSatateWnd GetDlgItem(IDC_CHK)->GetSafeHwnd();SetWindowTheme(hSatateWnd, _T(""), _T(""));}头文件中: afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);HBRUSH O…...

互联网 Java 面试八股文汇总(2025 最新整理)

我分享的这份 Java 后端开发面试总结包含了 JavaOOP、Java 集合容器、Java 异常、并发编程、Java 反射、Java 序列化、JVM、Redis、Spring MVC、MyBatis、MySQL 数据库、消息中间件 MQ、Dubbo、Linux、ZooKeeper、 分布式 &数据结构与算法等 25 个专题技术点,都…...

如何在 Ubuntu 中更新 Linux 内核

Linux内核是操作系统的基础,对操作系统的性能起着至关重要的作用。Linux开发人员通过补丁和更新不断增强内核,解决安全性、功能性和速度问题。及时了解这些发展对于确保机器以最佳方式运行至关重要。 本文教您如何使用三种不同的方法更新 Ubuntu 中的 L…...

FilterListenerAjax

今日目标: 能够使用 Filter 完成登陆状态校验功能能够使用 axios 发送 ajax 请求熟悉 json 格式,并能使用 Fastjson 完成 java 对象和 json 串的相互转换使用 axios + json 完成综合案例1,Filter 1.1 Filter概述 Filter 表示过滤器,是 JavaWeb 三大组件(Servlet、Filter、…...

27. 移除元素 C++

文章目录 一、题目链接二、参考代码三、所思所悟 一、题目链接 链接: 27. 移除元素 二、参考代码 思路1&#xff1a;找到需要移除的数组元素&#xff0c;将右侧全部元素向左覆盖 int removeElement(vector<int>& nums, int val) {int size nums.size();for (int …...

大模型专栏--Spring Ai Alibaba介绍和功能演示

Spring AI Alibaba 介绍和功能演示 背景 Spring AI Alibaba 开源项目基于 Spring AI 构建&#xff0c;是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践&#xff0c;提供高层次的 AI API 抽象与云原生基础设施集成方案&#xff0c;帮助开发者快速构建 AI 应用。…...

Redis设计与实现第17章 -- 集群 总结2(执行命令 重新分片)

17.3 在集群中执行命令 接收命令的节点会计算出命令要处理的数据库键属于哪个槽&#xff0c;并检查这个槽是否指派给了自己&#xff1a; 如果是的话&#xff0c;直接执行这个命令 否则&#xff0c;节点向客户端返回一个MOVED错误&#xff0c;指引客户端转向redirect至正确的节…...

微服务搭建----springboot接入Nacos2.x

springboot接入Nacos2.x nacos之前用的版本是1.0的&#xff0c;现在重新搭建一个2.0版本的&#xff0c;学如逆水行舟&#xff0c;不进则退&#xff0c;废话不多说&#xff0c;开搞 1、 nacos2.x搭建 1&#xff0c;首先第一步查询下项目之间的版本对照&#xff0c;不然后期会…...

3.建立本地仓库及常用命令

1.建立本地仓库 要使用Git对我们的代码进行版本控制&#xff0c;首先需要获得本地仓库 1&#xff09;在电脑的任意位置创建一个空目录&#xff0c;作为我们的本地Git仓库 2&#xff09;进入这个目录&#xff0c;右键点击Git Bash 窗口 3&#xff09;执行命令git init 4) 如果创…...

linux arm下获取屏幕事件(rk3588)

1、找到屏幕设备名称 cat /proc/bus/input/devices我的屏幕设备是ILITEK ILITEK-TP&#xff0c;它的设备名称是event1. 2、读取屏幕事件。 方法1&#xff1a; cat /dev/input/event1 | hexdump方法2&#xff1a; 3、c代码实现 #include <stdio.h> #include <unis…...

【机器学习】人工智能与气候变化:利用深度学习与机器学习算法预测和缓解环境影响

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f525;引言 1.1 背景介绍 1.2 人工智能与机器学习的崛起 1.3 本文内容概述 &#x1f528;气候变化的挑战 2.1 现今气候变化带来的影响和挑战 2.2 引发关注的气候变化趋势和数据 &#x1f916;人工智能…...

物联网射频识别和RFID开发(二):RFID原理及硬件组成

一、RFID无线识别的原理 &#xff08;一&#xff09;读写器与标签之间的无线电波交互方式 1、电感耦合 2、电磁反向散射耦合 &#xff08;二&#xff09;标签是如何将数据反馈给读写器的 1、电感耦合中的负载调试 2、电磁反向散射耦合中的负载调制 二、RFID无线通信中的调制…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

pycharm 设置环境出错

pycharm 设置环境出错 pycharm 新建项目&#xff0c;设置虚拟环境&#xff0c;出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...