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", "小猫");
    }

    @Override
    public 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() {
                @Override
                public 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

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

request
	userId: 1234
	position: "180E40N"

5.5 protobuffer(pb)

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据库】MySQL基本操作语句

目录 一、SQL语句 1.1 SQL分类 1.2 SQL语言规范 1.3 数据库对象与命名 1.3.1 数据库的组件(对象)&#xff1a; 1.3.2 命名规则&#xff1a; 1.4 SQL语句分类 二、基本命令 2.1 查看帮助信息 2.2 查看支持的字符集 2.3 查看默认使用的字符集 2.4 修改默认字符集 2.5…

Camera Raw:编辑 - 校准

Camera Raw “编辑”模块中的校准 Calibration面板设计初衷是校准相机所采集的 R、G、B 色彩信息&#xff0c;使相机的 RGB 色域范围尽可能与标准 RGB 色域范围重合。不过&#xff0c;现在多用于创意调色。通过调整红、绿、蓝三个原色的色相和饱和度&#xff0c;以及阴影的色调…

HTTP长连接

长连接优点 HTTP为什么要开启长连接呢? 主要是为了节省建立的时间,请求可以复用同一条TCP链路,不用重复进行三握+四挥 如果没有长连接,每次请求都做三握+四挥 如果有长链接,在一个 TCP 连接中可以持续发送多份数据而不会断开连接,即请求可以复用TCP链路 长连接缺点 …

数字信号处理及MATLAB仿真(3)——量化的其他概念

上回书说到AD转换的两个步骤——量化与采样两个步骤。现在更加深入的去了解以下对应的概念。学无止境&#xff0c;要不断地努力才有好的收获。万丈高楼平地起&#xff0c;唯有打好基础&#xff0c;才能踏实前行。 不说了&#xff0c;今天咱们继续说说这两个步骤&#xff0c;首先…

归并排序的实现(递归与非递归)

概念 基本思想&#xff1a;归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使…

一篇文章搞懂qt图形视图框架setRect和setPos函数的意义

setRect()函数 三个坐标系我就不多说了&#xff0c;view原点默认在左上角&#xff0c;scene和item的原点默认都在中心位置。 注意&#xff1a;此函数并不能设置一个item的位置&#xff0c;我的理解是当一个item调用该函数时&#xff0c;会构建一个一个item的坐标系&#xff0c…

秋招提前批面试经验分享(下)

⭐️感谢点开文章&#x1f44b;&#xff0c;欢迎来到我的微信公众号&#xff01;我是恒心&#x1f60a; 一位热爱技术分享的博主。如果觉得本文能帮到您&#xff0c;劳烦点个赞、在看支持一下哈&#x1f44d;&#xff01; ⭐️我叫恒心&#xff0c;一名喜欢书写博客的研究生在读…

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

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

关于Web开发的详细介绍

目录 一、什么是Web&#xff1f; 二、Web网站的工作流程和开发模式 &#xff08;1&#xff09;简单介绍 &#xff08;2&#xff09;工作流程 1、第一步 2、第二步 &#xff08;3&#xff09;Web网站的开发模式 1、前后端分离开发模式 ​编辑2、混合开发模式 三、开发W…

DPDK源码分析之(1)libmbuf模块

DPDK源码分析之(1)libmbuf模块 Author&#xff1a;OnceDay Date&#xff1a;2024年7月2日 漫漫长路&#xff0c;有人对你笑过嘛… 全系列文档可参考专栏&#xff1a;源码分析_Once-Day的博客-CSDN博客 参考文档&#xff1a; DPDK downloadGetting Started Guide for Linux…

业界数据架构的演变

目录 一、概述 二、业务处理-单体架构 三、业务处理-微服务架构 四、数据分析-大数据Lambda架构 五、数据分析-Kappa架构 六、数据分析-LambdaKappa混合架构 七、湖仓一体架构 一、概述 近年来随着越来越多的大数据技术被开源&#xff0c;例如&#xff1a;HDFS、Spark等…

最小表示法

#define _CRT_SECURE_NO_WARNINGS #include<bits/stdc.h> using namespace std;const int N (int)3e5 5; int n; int a[N * 2];int main() {cin >> n;for (int i 0; i < n; i) {cin >> a[i];a[i n] a[i]; // 构造成链}int l 0, r 1; // 一开始 r …

进入防火墙Web管理页面(eNSP USG6000V)和管理员模块

1、进入防火墙Web管理页面 USG系列是华为提供的一款高端防火墙产品&#xff0c;其特点在于提供强大的安全防护能力和灵活的扩展性。 以eNSP中的USG6000为例&#xff1a; MGMT口&#xff08;web管理口&#xff09;&#xff1a;对应设备上的G0/0/0口&#xff0c;上面初始配有一…

算法-常见数据结构设计

文章目录 1. 带有setAll功能的哈希表2. LRU缓存结构3. O(1)时间插入删除随机(去重)4. O(1)时间插入删除随机(不去重)5. 快速获取数据流中的中位数6. 最大频率栈7. 全O(1)结构8. LFU缓存结构 本节的内容比较难, 大多是leetcodeHard难度级别的题目 1. 带有setAll功能的哈希表 哈希…

QCustomPlot+ vs2022+ qt

零、printSupport 步骤一&#xff1a;下载QCustomPlot 访问QCustomPlot的官网 QCustomPlot 下载最新版本的源代码。 步骤二&#xff1a;配置项目 创建新的Qt项目&#xff1a; 打开VS2022&#xff0c;创建一个新的Qt Widgets Application项目。 将QCustomPlot源代码添加到项目…

集合复习(java)

文章目录 Collection 接口Collection结构图Collection接口中的方法Iterator 与 Iterable 接口Collection集合遍历方式迭代器遍历增强 for 遍历 List&#xff08;线性表&#xff09;List特有方法ArrayList&#xff08;可变数组&#xff09;ArrayList 底层原理ArrayList 底层原理…

【UML用户指南】-30-对体系结构建模-模式和框架

目录 1、机制 2、框架 3、常用建模技术 3.1、对设计模式建模 3.2、对体系结构模式建模 用模式来详述形成系统体系结构的机制和框架。通过清晰地标识模式的槽、标签、按钮和刻度盘 在UML中&#xff0c; 对设计模式&#xff08;也叫做机制&#xff09;建模&#xff0c;将它…

前端技术(三)—— javasctipt 介绍:jQuery方法和点击事件介绍(补充)

6. 常用方法 ● addClass() 为jQuery对象添加一个或多个class <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&…

Efficient Contrastive Learning for Fast and Accurate Inference on Graphs

发表于:ICML24 推荐指数: #paper/⭐⭐⭐ 创新点一颗星,证明三颗星(证明的不错,值得借鉴,但是思路只能说还行吧) 如图, 本文采取的创新点就是MLP用原始节点,GCN用邻居节点的对比学习.这样,可以加快运算速度 L E C L − 1 ∣ V ∣ ∑ v ∈ V 1 ∣ N ( v ) ∣ ∑ u ∈ N ( v )…

汇聚荣拼多多电商的技巧有哪些?

在电商平台上&#xff0c;汇聚荣拼多多以其独特的商业模式和创新的营销策略吸引了大量消费者。那么&#xff0c;如何在这样一个竞争激烈的平台上脱颖而出&#xff0c;成为销售佼佼者呢?本文将深入探讨汇聚荣拼多多电商的成功技巧。 一、精准定位目标客户群体 首先&#xff0c;…