JavaEE初阶-网络编程

文章目录

  • 前言
  • 一、UDP与TCP
    • 1.1 有连接与无连接
    • 1.2 全双工
    • 1.3 可靠传输与不可靠传输
    • 1.4 面向子节流与面向数据报
  • 二、UDP回显服务器及客户端编写
  • 三、UDP字典服务器
  • 四、TCP回显服务器及客户端编写
  • 五、数据序列化的方式
    • 5.1 基于行文本的方式传输
    • 5.2 基于XML的格式
    • 5.3 基于json
    • 5.4 yml
    • 5.5 protobuffer(pb)


前言

目前见到socket这个词,就可以认为是网络编程api的统称。操作系统提供的socket api不是一套而是有好几套。
(1)流式套接字:给TCP使用。
(2)数据包套接字:给UDP使用。
(3)Unix域套接字:不能跨主机通信,只是本地主机上的线程与线程之间的通信方式。

一、UDP与TCP

UDP和TCP都是传输层的协议,是给应用层提供服务的,但是两个协议之间的差异还是很大的。

1.1 有连接与无连接

UDP是无连接的,TCP是有连接的。
举例子来理解的话就是有连接相当于打电话,你必须等到对方接通你才能和他通话;无连接相当于短信,你想发就发,不用先接通。
对于UDP的无连接就是直接发送数据,TCP的有连接就是建立连接之后才能发送数据。
另外在计算机中的连接是一个抽象的概念,生活中我们谈到连接往往是把两个东西连起来,但是在计算机的领域,连接就是认为建立连接的双方各自保存对方的信息,此时就认为是建立了一个抽象的连接。

1.2 全双工

UDP和TCP都是全双工的。
至于全双工就是指一条通信链路能够双向通信,与之相对的就是半双工指的就是一条通信链路只能够单向通信。
对于全双工在代码中的体现就是后续使用socket既可以读也可以写。

1.3 可靠传输与不可靠传输

UDP是不可靠传输,TCP是可靠传输。
可靠指的不是一定百分百安全,只是说TCP会尽力去保证数据在传输的过程中少发生意外,尽可能多的去保留数据。而UDP就不一样了,它是不可靠的,只要把数据发送过去就行了,不管在这个过程中会发生什么。
通过上述不难发现,可靠与不可靠也决定了UDP与TCP的一部分特点。TCP要想实现可靠性必然会付出代价,因此它数据传输的速度是不如UDP的,但是UDP的可靠性也因此不如TCP。两者有着不同的适用场景,TCP适用于对数据要求高的,不能出错的场景,UDP适用于对数据的准确性要求不高,但是对传输速率要求高的场景。一般来说的话现在一些应用软件都是UDP和TCP混用。

1.4 面向子节流与面向数据报

文件操作是面向字节流的,TCP与其有着相同的特点也是面向字节流的,UDP则是面向数据报的。面向数据报就是指数据传输的单位是数据报,一次读写只能读写完整的数据报,不能搞半个也不搞一个半。
另外网络传输数据的基本单位涉及到几个术语:
(1)数据报Datagram UDP
(2)数据段Segment TCP
(3)数据包 Packet IP
(4)数据帧 Frame 数据链路层
虽然以上术语是有差别的,但是程序员对于以上术语都是混着用的不会做刻意区分,因此后续使用过程中也不必刻意区分。

二、UDP回显服务器及客户端编写

Echo称为“回显”,正常的服务器你给它发送不同的请求就会返回不同的响应,此处回显的意思就是请求是什么就返回什么。实际上这就是最简单的客户端服务器程序,用来认识socket api的用法。
UdpEchoServer:

package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket datagramSocket = null;public UdpEchoServer(int port) throws SocketException {datagramSocket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器启动!!!");while (true) {// 1.读取请求并且解析DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);datagramSocket.receive(requestPacket);String request = new String(requestPacket.getData(), 0, requestPacket.getLength());// 2.处理计算请求信息String response = this.process(request);// 3.把响应返回客户端 客户端的ip以及端口号可以通过请求的数据包中获取DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), 0, response.getBytes().length,requestPacket.getSocketAddress());datagramSocket.send(responsePacket);// 打印日志 ip port 请求以及返回内容System.out.printf("[%s:%d] req=%s reps=%s", requestPacket.getAddress(), requestPacket.getPort(), request, response);System.out.println();}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer udpEchoServer = new UdpEchoServer(4090);udpEchoServer.start();}}

UdpEchoClient:

package network;import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket datagramSocket = null;private String serverIp;private int port;public UdpEchoClient(String ip, int port) throws SocketException {datagramSocket = new DatagramSocket();this.serverIp = ip;this.port = port;}public void start() throws IOException {System.out.println("客户端启动!!!");Scanner sc = new Scanner(System.in);while (true) {// 输入请求System.out.println("请输入请求:");String request = sc.nextLine();// 打包请求并且发送请求DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), 0, request.getBytes().length, InetAddress.getByName(serverIp), port);datagramSocket.send(requestPacket);// 接收响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);datagramSocket.receive(responsePacket);// 打印响应的内容String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.println("响应:" + response);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("localhost", 4090);client.start();}}

三、UDP字典服务器

对于字典服务器,和回显的区别在于你请求的是一个中文字符串,响应也就是要返回一个英语单词,因此我们需要在服务器端去存储对应的单词键值对即可,又因为我们前面实现了回显服务器,所以我们可以直接继承回显服务器的代码,然后添加单词键值对并且重写posses函数即可,至于客户端还以一样不需要去改变。
UdpDictServer:

package network;import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;public class UdpDictServer extends UdpEchoServer {private HashMap<String, String> dict = null;public UdpDictServer(int port) throws SocketException {super(port);dict = new HashMap<>();dict.put("hello", "你好");dict.put("pig", "小猪");dict.put("dog", "小狗");dict.put("cat", "小猫");}@Overridepublic String process(String request) {return (String) dict.getOrDefault(request, "未搜索到单词");}public static void main(String[] args) throws IOException {UdpDictServer dictServer = new UdpDictServer(4090);dictServer.start();}
}

四、TCP回显服务器及客户端编写

TcpEchoServer:

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TcpEchoServer {ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!!!");while (true) {// 建立连接Socket clientSocket = serverSocket.accept();// 建立线程池 这里建立的是可以自动扩容的线程池ExecutorService pool=Executors.newCachedThreadPool();// 为了方便多个客户端对服务器发起请求// 这里使用主线程来处理这里的循环 然后使用多线程的放式去去处理每一个客户端的请求
//            Thread t = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }
//
//            });
//
//            t.start();// 使用线程池的方式pool.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}});}}private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 获取字节流对象try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {Scanner sc = new Scanner(inputStream);while (true) {// 两种情况if (!sc.hasNext()) {System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 获取请求String request = sc.next();// 处理请求 String response = process(request);// 返回请求outputStream.write(response.getBytes());// 服务器打印日志System.out.printf("[%s:%d] req=%s resp=%s", clientSocket.getInetAddress(), clientSocket.getPort(), request, response);}} catch (IOException e) {throw new RuntimeException(e);} finally {// 每次一个客户端请求的连接最后都要关闭 否则当多个客户端连接同一个服务器的时候就会出现文件描述符表爆满的问题// 这个问题简单想一下就会理解clientSocket.close();}}private String process(String request) {return request + '\n';}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(4090);server.start();}}

TcpEchoClient:

package network;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class TcpEchoClient {Socket socket = null;public TcpEchoClient(String ip, int port) throws IOException {// 这里根据ip和port号自动和服务器建立连接// 具体完成的操作都是系统内核完成的socket = new Socket(ip,port);}private void start() {System.out.println("客户端启动!!!");Scanner sc = new Scanner(System.in);// 获取字节流对象try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) {Scanner scNetwork = new Scanner(inputStream);while (true) {System.out.println("请输入要发送的内容:");// 输入请求String request = sc.next();request += '\n';// 发送请求outputStream.write(request.getBytes());// 两中情况// 第一种:tcp连接断开 返回false// 第二种:有请求返回if (!scNetwork.hasNext()) {break;}String response = scNetwork.next();System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("localhost", 4090);client.start();}}

在tcp服务器代码的编写中我们发现服务器无法去应对多个客户端的请求,因此我们使用多线程或者线程池的方式,让每一个线程去处理一个请求。此时不免去联想到一个问题如果在一个场景当中,服务器收到的不同客户端的请求越来越多,我们难道需要不停的去创建线程吗,如果真是这样服务器肯定支撑不住。事实上对于这种情况可以使用IO多路复用+分布式的方法。
分布式我们都知道,那么IO多路复用指的是什么?其实就是使用一个线程去管理多个socket(可以将socket理解成客户端的请求),这些socket往往不是同时有数据需要处理,而是同一时刻只有少数的socket需要去读取数据。

五、数据序列化的方式

5.1 基于行文本的方式传输

这种格式是自定义的,只要确保客户端与服务器使用的是同一套规则即可。缺点就是不好用,可维护性差。

5.2 基于XML的格式

XML是通过成对的标签来进行组织的。

<request><userId>1234</userId>
</request>

5.3 基于json

当前最流行最广泛的使用方式,是以键值对的形式,可读性非常好而且比XML简洁。

{userId: 1234
}

5.4 yml

和前两种是类似的,是基于缩进的格式,使用缩进来表达包含/嵌套关系。

requestuserId: 1234position: "180E40N"

5.5 protobuffer(pb)

前面几种说到底还是文本格式,肉眼还能看懂,这里的pb就是二进制格式了,可以对数据进行进一步的整理和压缩,虽然可读性不好但是对空间进行最充分的利用,节省网络带宽,效率也最高,适用于对传输效率高的场景。

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

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

相关文章

STM32芯片系列与产品后缀解读

一. 产品系列 STM32单片机是一系列基于ARM Cortex-M内核的32位微控制器&#xff0c;广泛应用于嵌入式系统中。 STM32系列由STMicroelectronics&#xff08;意法半导体&#xff09;开发和生产&#xff0c;并凭借其灵活的设计、丰富的外设和强大的生态系统&#xff0c;成为嵌入式…

咬文嚼字:词元是当今生成式人工智能失败的一个重要原因

生成式人工智能模型处理文本的方式与人类不同。了解它们基于"标记"的内部环境可能有助于解释它们的一些奇怪行为和顽固的局限性。从 Gemma 这样的小型设备上模型到 OpenAI 业界领先的 GPT-4o 模型&#xff0c;大多数模型都建立在一种称为转换器的架构上。由于转换器在…

Ubuntu24.04清理常见跟踪软件tracker

尽量一天一更&#xff0c;不刷视频&#xff0c;好好生活 打开系统监视器&#xff0c;发现开机有个tracker-miner-fs-fs3的跟踪程序&#xff0c;而且上传了10kb的数据。 搜索知&#xff0c;该程序会搜集应用和文件的信息。 删除tracker 显示带tracker的apt程序 sudo apt lis…

ThreadLocal的内存泄漏

什么是内存泄漏 程序在申请内存后&#xff0c;无法释放已申请的内存空间在定义变量时&#xff0c;需要一段内存空间来存储数据信息&#xff0c;而这段内存如果一直不被释放&#xff0c;那么就会导致内存被占用光&#xff0c;而被占用的这个对象&#xff0c;一直不能被回收掉&am…

书生·浦语2.5开源,推理能力再创新标杆

导读 2024 年 7 月 3 日&#xff0c;上海人工智能实验室与商汤科技联合香港中文大学和复旦大学正式发布新一代大语言模型书⽣浦语2.5&#xff08;InternLM2.5&#xff09;。相比上一代模型&#xff0c;InternLM2.5 有三项突出亮点&#xff1a; 推理能力大幅提升&#xff0c;在…

VUE与React的生命周期对比

前言 在前端开发中&#xff0c;Vue和React是两个非常流行的JavaScript框架&#xff0c;它们各自有着独特的生命周期机制。了解并熟练掌握这些生命周期&#xff0c;对于开发高效、可维护的前端应用至关重要。本文将详细对比Vue和React的生命周期&#xff0c;帮助开发者更好地理…

Python | Leetcode Python题解之第222题完全二叉树的节点个数

题目&#xff1a; 题解&#xff1a; # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def countNodes(self,…

好玩的珠玑妙算-加作弊带概率空间+日志存储240705mindMaster

Python代码 import random import time import datetimeNUM_DIGITS 10 #NUM_NON_ZERO_DIGITS 9failFlag 0class Mastermind:def __init__(self, code_length, max_attempts, secret01code, game_id): # def __init__(self, code_length, max_attempts):self.code_length…

【Elasticsearch】Elasticsearch倒排索引详解

文章目录 &#x1f4d1;引言一、倒排索引简介二、倒排索引的基本结构三、Elasticsearch中的倒排索引3.1 索引和文档3.2 创建倒排索引3.3 倒排索引的存储结构3.4 词典和倒排列表的优化 四、倒排索引的查询过程4.1 过程4.2 示例 五、倒排索引的优缺点5.1 优点5.2 缺点 六、倒排索…

【Excel】求和带文字的数据

目录标题 1. 给出样例2. CtrlE3. CtrlH → A替换为 → 全部替换 1. 给出样例 2. CtrlE 3. CtrlH → A替换为 → 全部替换

算法期末函数题

R6-1 可重复选择的组合数问题 【考核知识点】可重复选择的组合计数 【问题描述】 有n个不同元素&#xff08;1<n<20&#xff09;&#xff0c;每个元素可以选多次&#xff0c;一共需要选出k个元素出来&#xff08;1<k<20&#xff09;&#xff0c;问有多少种选取的…

监控易V7.6.6.15升级详解2:设备管理功能

随着企业IT架构的日益复杂&#xff0c;对设备管理的需求也在不断提升。为了满足广大用户对于设备管理的高效、精准需求&#xff0c;我们荣幸地宣布监控易系统已完成了一次重要的版本升级。本次升级不仅优化了原有功能&#xff0c;还新增了一系列实用特性&#xff0c;旨在为用户…

仿qq音乐播放微信小程序模板源码

手机qq音乐应用小程序&#xff0c;在线音乐播放器微信小程序网页模板。包含&#xff1a;音乐歌曲主页、推荐、排行榜、搜索、音乐播放器、歌单详情等。 仿qq音乐播放微信小程序模板源码

【ubuntu自启shell脚本】——在ubuntu中如何使用系统自带的启动应用程序设置开机自启自己的本地shell脚本

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、设置开机自启shell脚本1.使用 gnome-session-properties2.测试的shell例程代码 总结 前言 在Ubuntu系统中设置开机自启脚本是一种重要的自动化方法。开机自…

YOLO-World实时开集检测论文阅读

论文&#xff1a;《YOLO-World: Real-Time Open-Vocabulary Object Detection》 代码&#xff1a;https://github.com/AILab-CVC/YOLO-World 1.Abstract 我们介绍了YOLO World&#xff0c;这是一种创新的方法&#xff0c;通过在大规模数据集上进行视觉语言建模和预训练&#…

js之弹性布局使用方法

弹性布局&#xff08;Flexbox&#xff09;是一种现代化的 CSS 布局方法&#xff0c;它可以让您更方便地创建响应式和动态布局。在本篇文档中&#xff0c;我们将介绍弹性布局的基本概念以及如何在项目中使用它。 一、基本概念 容器&#xff08;Container&#xff09;&#xff…

WPF中逻辑树和视觉树

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;“逻辑树”&#xff08;Logical Tree&#xff09;和“可视树”&#xff08;Visual Tree&#xff09;是两个重要的概念&#xff0c;它们代表了不同的对象层次结构&#xff0c;用于描述应用程序的组织…

洛谷 [SNCPC2024] 写都写了,交一发吧 题解

分析 显然&#xff0c;两个相同的数去按位与的结果还是该数。 由于一个代码可以提交多次&#xff0c;那么可以把得分最高的代码提交两次&#xff0c;这样的得分就是这个代码的得分&#xff0c;很明显&#xff0c;这样是最优的。 Code #include<iostream> using names…

STM32微控制器的SPI存储解决方案:W25Q64 Flash存储器深度应用

摘要 在嵌入式系统设计中&#xff0c;存储解决方案对于数据的持久化至关重要。W25Q64 Flash存储器以其高效的存储能力和与SPI总线的兼容性&#xff0c;成为STM32微控制器项目中的优选。本文将深入探讨STM32微控制器的SPI存储解决方案&#xff0c;重点介绍W25Q64 Flash存储器的…

vue3+antd 实现点击按钮弹出对话框

格式1&#xff1a;确认对话框 按钮&#xff1a; 点击按钮之后&#xff1a; 完整代码&#xff1a; <template><div><a-button click"showConfirm">Confirm</a-button></div> </template> <script setup> import {Mod…