回显服务器

.

在这里插入图片描述

写一个应用程序,让这个程序可以使用网络通信,这里就需要调用传输层提供的api,传输层提供协议,主要是两个:
UDP,TCP,它们分别提供了一套不同的api,socket api.

UDP和TCP

UDP:无连接,不可靠传输,面向数据报,全双工
TCP:有连接,可靠传输,面向字节流,全双工
一个客户端可以连接多个服务器,一个服务器也可以连接多个客户端(多对多)

连接(connection)

链接(link)快捷方式
windows上只有软链接(符号链接),通过一个文件,文件的内容保存另一个文件的路径,实现软链接
Linux除了支持软连接,还支持硬链接(两个路径,共享同一个inode节点)

可靠传输和不可靠传输

可靠传输,不是说,A给B发信息100%能收到,而是A尽可能的把消息传给B,并且在传输失败或者成功的时候,A能够感知得到
这里的可靠与否,不能说明谁好谁坏,可靠传输想要实现,也是需要一定的成本,所以,有舍才有的,我们应该将这两种属性视为各自的特性
TCP和文件操作类似,都是"流"式的(由于这里传输的单位是字节,称为字节流)
比如:
通过TCP读写100字节的数据
可以一次读100字节
一次读写50字节,分两次
一次读写10字节,分10次
UDP是面向数据报,读写的基本单位,是一个UDP数据报(包含了一系列是数/属性)
全双工:一个通道,可以双向通信
半双工:一个通道,只能单向通信
UDP更简单,先认识UDP的socket api
两个核心的类:
1.DatagramSocket
操作系统,使用文件这样的概念,来管理一些硬件资源,网卡,操作系统也是使用文件的方式来管理网卡的,
表示网卡这样的文件,称为socket文件
Java中的socket对象,对应着系统里的socket文件(最终还是要落到网卡上)
要进行网络通信,必须得先有socket对象
在这里插入图片描述
一个客户端的主机,上面运行的程序有很多,可能存在手动指定的端口号被别的程序占用的了,让系统分配,更为合适
在这里插入图片描述

2.DatagramPacket
表示UDP数据报,代表了系统中设定的UDP数据报的二进制数据

方法签名方法说明
DatagramPacket(byte[]buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[]buf, int offset, int length,SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号

在这里插入图片描述
接下来,开始手动编写一个UDP客户端服务器

回显服务器

UDP版本

服务器

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoSerevr {
    DatagramSocket socket = null;
    public UdpEchoSerevr(int port) throws SocketException {
        //这里抛出的异常,最常见的情况是端口号被占用
        socket = new DatagramSocket(port);
    }
    //使用下面的start取调用服务器
    public void start() throws IOException {
        System.out.println("服务器启动!");
        //反复的,长期的执行针对客户端请求处理的逻辑.
        while(true){
            DatagramPacket packet = new DatagramPacket(new byte[4096],4096);
            //一个服务器,在执行过程中,主要执行的是三个核心任务
            //1.读取请求,并解析
            //下面这个方法中的参数DatagramPacket是一个"输出型参数"
            //传入receive的是一个空的对象
            //receive内部就会把这个空的对象的内容给填充上,
            //当receive执行结束的时候,就会得到一个装满内容的DatagramPacket
            socket.receive(packet);
            //要将数据报转换为字符串的前提是,客户端发的数据就是一个文本的字符串
            String request = new String(packet.getData(),0,packet.getLength());
            //2.根据请求,计算响应
            String response = process(request);
            //3.把响应写回给客户端
            //此时要告知网卡,要发送给客户端的内容是什么,要发给谁
            //getSocketAddress可以得到客户端主机的ip和端口号
            DatagramPacket responseRequest = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    packet.getSocketAddress());
            socket.send(responseRequest);
            System.out.printf("[%s:%d] req: %s,resp: %s\n",packet.getAddress().toString(),packet.getPort(),request,response);
        }
    }
    public String process(String request){
        return request;
    }
    public static void main(String[] args) throws IOException {
        UdpEchoSerevr serevr = new UdpEchoSerevr(9090);
        serevr.start();
    }
}

客户端

package network;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    DatagramSocket socket = null;
    private String serevrIP;
    private int serverPort;
    public UdpEchoClient(String IP,int Port) throws SocketException {
        serevrIP = IP;
        serverPort = Port;
        socket = new DatagramSocket();
    }
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("客户端启动!");
        while(true){
            System.out.println("->");
            String request = scanner.next();
            //构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。
            //构造请求对象,并发给服务器
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serevrIP),serverPort);
            socket.send(requestPacket);
            //读取服务器的响应,并解析出响应内容
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            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("192.168.38.196" ,9090);
        client.start();
    }
}

字典服务器

package network;

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;

public class UdpditServer extends UdpEchoSerevr{
    public static HashMap<String,String> hashMap = new HashMap<>();
    public UdpditServer(int port) throws SocketException {
        super(port);
        hashMap.put("cat","猫");
        hashMap.put("dog","狗");
        hashMap.put("girl","女孩");
        hashMap.put("boy","男孩");
    }

    @Override
    public String process(String request) {
        return hashMap.getOrDefault(request,"没有查到相关信息");
    }

    public static void main(String[] args) throws IOException {
        UdpditServer udpditServer = new UdpditServer(9090);
        udpditServer.start();
    }
}

TCP版本

分量要比UDP更重,用到更多的协议
TCP提供的API主要也是有两个类:
ServerSocket(给服务器使用的socket)
Socket(既会给服务器使用,也会给客户端使用)
这里不需要专门的类来表示TCP数据报,因为TCP是进行字节流传输的,是一个字节一个字节的进行传输的,而一个TCP数据报,就是一个字节数组

服务器

package network;

import java.io.*;
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 {
    //创建线程池,方便多线程实现多个客户端同时访问服务器
    private ExecutorService service = Executors.newCachedThreadPool();
    private ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket =new ServerSocket(port);
    }
    public void start() throws IOException {
        //进入start方法后,第一件事不是读取客户端的请求,而是处理客户端的"连接"
        //内核中的"连接",就像一个一个"代办事项",这些代办事项在一个队列 的数据结构中,
        //应用程序就要一个一个的完成这些任务,要完成任务,就要先取出这些任务
        //取出任务,需要用到下面这个方法
        //当客户端和服务器进行数据交互的时候,就会进行"握手"
        //"握手"成功后,就会生成一个这样的代办事项,称为"连接"
        //客户端的应用程序在生成serversocket文件的时候,socket内部会有一个队列,用来管理上述的"连接"
        //每个socket都有这样一个队列,各自独立
        //当没有客户端的时候,就会进入阻塞
        Socket clientSocket = serverSocket.accept();
        //accept是把内核中已经建立好的连接,从serverSocket的队列中给拿到应用程序中,但是这里的返回值并非是一个"connection"对象
        //而是一个socket对象,通过这个socket对象和对方进行网络通信
        service.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    //通过这个方法,来处理连接的逻辑
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d]客户端上线!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        //接下来,就可以进行读取请求,根据请求计算响应,返回响应
        //socket内部包含两个字节流对象,可以把这两个字节流获取到,完成后续的工作
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            //一次连接中,可能会涉及到多次请求/响应
            while (true){
                //1.读取请求并解析,为了方便读取,直接使用Scanner
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()){
                    //读取完毕,客户端下线
                    System.out.printf("[%s:%d]客户端下线!",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                //这个代码暗含一个规定,客户端发过来的数据,必须是一个文本数据,同时,还得带有空白符作为分割(比如换行)
                String request = scanner.next();//next就是读取数据,一直读到空白符结束
                //2.根据请求计算响应
                String response = process(request);
                //3.把响应写回给客户端,把outputStream使用PrinterWriter包裹一下,方便进行发数据
                PrintWriter writer = new PrintWriter(outputStream);
                //此处使用PrintWriter的println方法,把响应返回给客户端
                //次数用println就是为了在结尾加上一个\n,为方便客户端读取响应,使用Scanner.next读取
                writer.println(response);
                //这里还需要加一个"刷新缓冲区"的操作
                //IO操作是比较消耗资源的,相比于访问内存,进行IO次数越多,程序的速度就越慢,
                //使用一块内存作为缓冲区,写数据的时候,先写道缓冲区内进行一波缓冲,统一进行IO
                //PrintWriter内置了缓冲区,手动刷新,确保这里的数据是真的通过网卡发出去了,而不是残留在内存缓冲区中
                writer.flush();//不加这一句也不一定错,缓冲区满了的时候,也会触发刷新,程序退出也会
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            //每次读取一个连接,都会创建一个socket,会很占用内存,因此,需要确保及时被close
            clientSocket.close();
        }
    }
    public String process(String request){
        return request;
    }
    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.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 Socket socket = null;
    //要和服务器通信,就需要知道,服务器所在的位置
    public TcpEchoClient(String ServerIp,int ServerPort) throws IOException {
        //这个new操作之后,就完成了TCP的连接
        socket = new Socket(ServerIp,ServerPort);
    }
    public void start(){
        System.out.println("客户端启动!");
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()){
            while (true){
                //1.从控制台输入字符串
                System.out.println("->");
                Scanner scanner = new Scanner(System.in);
                String request = scanner.next();
                //2.把请求发送给服务器
                PrintWriter writer = new PrintWriter(outputStream);
                writer.println(request);
                writer.flush();
                //3.读取服务器的响应,并打印
                Scanner scanner1 = new Scanner(inputStream);
                String response = scanner1.next();
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();
    }
}

解决高并发问题:
1.开源:引入更多的硬件资源.
2.节流:提供单位硬件资源能够处理的请求数(IO多路复用)

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

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

相关文章

仪表板展示|DataEase看中国:历年研究生报考数据分析

背景介绍 在信息时代的浪潮中&#xff0c;研究生教育作为培养高层次专业人才的重要通道&#xff0c;不断吸引着广大毕业生和在职人士的关注。今天我们结合2018年&#xff5e;2024年的研究生报考数据&#xff0c;以数字为镜&#xff0c;深入了解近年来研究生培养态势。 本文将…

Redis篇----第七篇

系列文章目录 文章目录 系列文章目录前言一、Redis 的回收策略(淘汰策略)?二、为什么 edis 需要把所有数据放到内存中?三、Redis 的同步机制了解么?四、Pipeline 有什么好处,为什么要用 pipeline?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍…

机器学习:逻辑回归原理

逻辑回归模型是一种广泛应用于分类问题的统计方法。尽管名为“回归”&#xff0c;但它实际上是一种分类算法&#xff0c;主要用于预测观察对象属于某个类别的概率。逻辑回归模型特别适用于二分类问题&#xff0c;但也可以通过一些策略扩展到多分类问题。 逻辑回归的应用与优化…

gazebo 中使用gmaping 建图

一、使用gmapping 建图 启动roscoreroslaunch wpr_simulation wpb_stage_robocup.launchsudo apt install ros-noetic-gmappingrosrun gmapping slam_gmapping启动rviz (rosrun rviz rviz)。添加RobotModel、LaserScan、Map后 显示如下&#xff1a; 6.rosrun wpr_simulation k…

网络爬虫基础(上)

1. 爬虫的基本原理 爬虫就是在网页上爬行的蜘蛛&#xff0c;每爬到一个节点就能够访问该网页的信息&#xff0c;所以又称为网络蜘蛛&#xff1b; 网络爬虫就是自动化从网页上获取信息、提取信息和保存信息的过程&#xff1b; 2. URL的组成部分 URL全称为Uniform Resource L…

UG NX二次开发(C#)-PMI-获取PMI尺寸数据

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG NX的三维模型中添加PMI尺寸信息3、采用二次开发获取尺寸数据4、测试结果1、前言 PMI(Product and Manufacturing Information)是产品和制造信息的简称,主要用于将产品部件设计的…

python 与 neo4j 交互(py2neo 使用)

参考自&#xff1a;neo4j的python.py2neo操作入门 官方文档&#xff1a;The Py2neo Handbook — py2neo 2021.1 安装&#xff1a;pip install py2neo -i https://pypi.tuna.tsinghua.edu.cn/simple 1 节点 / 关系 / 属性 / 路径 节点(Node)和关系(relationship)是构成图的基础…

代码随想录算法训练营第二十三天 | 669. 修剪二叉搜索树,108.将有序数组转换为二叉搜索树,538.把二叉搜索树转换为累加树 [二叉树篇]

代码随想录算法训练营第二十三天 LeetCode 669. 修剪二叉搜索树题目描述思路递归参考代码 LeetCode 108.将有序数组转换为二叉搜索树题目描述思路参考代码 LeetCode 538.把二叉搜索树转换为累加树题目描述思路参考代码 LeetCode 669. 修剪二叉搜索树 题目链接&#xff1a;669. …

《Solidity 简易速速上手小册》第9章:DApp 开发与 Solidity 集成(2024 最新版)

文章目录 9.1 DApp 的架构和设计9.1.1 基础知识解析更深入的理解实际操作技巧 9.1.2 重点案例&#xff1a;去中心化社交媒体平台案例 Demo&#xff1a;创建去中心化社交媒体平台案例代码SocialMedia.sol - 智能合约前端界面 测试和验证拓展功能 9.1.3 拓展案例 1&#xff1a;去…

LabVIEW高速信号测量与存储

LabVIEW高速信号测量与存储 介绍了LabVIEW开发的高速信号测量与存储系统&#xff0c;解决实验研究中信号捕获的速度和准确性问题。通过高效的数据处理和存储解决方案&#xff0c;本系统为用户提供了一种快速、可靠的信号测量方法。 项目背景 在科学研究和工业应用中&#xf…

百度RT-DETR :基于视觉变换器的实时物体检测器

概述 实时检测转换器 (RT-DETR) 由百度开发&#xff0c;是一种尖端的端到端物体检测器&#xff0c;可在保持高精度的同时提供实时性能。它利用视觉转换器&#xff08;ViT&#xff09;的强大功能&#xff0c;通过解耦尺度内交互和跨尺度融合&#xff0c;高效处理多尺度特征。RT…

think-cell Round 1

think-cell Round 1 A. Maximise The Score 题意&#xff1a;给出2n个数&#xff0c;每次选两个取较小值加到分数里&#xff0c;分数最大为多少。 思路&#xff1a;排序&#xff0c;奇数位和。 AC code&#xff1a; void solve() {cin >> n;int ans 0;int a[N];for…

EXCEL使用VBA一键批量转换成PDF

EXCEL使用VBA一键批量转换成PDF 上图是给定转换路径 Sub 按钮1_Click() Dim a(1 To 1000) As String Dim a2 As String Dim myfile As String Dim wb As Workbook a2 Trim(Range("a2"))myfile Dir(a2 & "\" & "*.xls")k 0Do While m…

如何创建WordPress付款表单(简单方法)

您是否正在寻找一种简单的方法来创建付款功能WordPress表单&#xff1f; 小企业主通常需要创建一种简单的方法来在其网站上接受付款&#xff0c;而无需设置复杂的购物车。简单的付款表格使您可以轻松接受自定义付款金额、设置定期付款并收集自定义详细信息。 在本文中&#x…

django请求生命周期流程图,路由匹配,路由有名无名反向解析,路由分发,名称空间

django请求生命周期流程图 浏览器发起请求。 先经过网关接口&#xff0c;Django自带的是wsgiref&#xff0c;请求来的时候解析封装&#xff0c;响应走的时候打包处理&#xff0c;这个wsgiref模块本身能够支持的并发量很少&#xff0c;最多1000左右&#xff0c;上线之后会换成u…

算法项目(1)—— LSTM+CNN+四种注意力对比的股票预测

本文包含什么? 项目运行的方式(包教会)项目代码(在线运行免环境配置)不通注意力的模型指标对比一些效果图运行有问题? csdn上后台随时售后.项目说明 本项目实现了基于CNN+LSTM构建模型,然后对比不同的注意力机制预测股票走势的效果。首先看一下模型结果的对比: 模型MS…

springboot当中使用EMQX(MQTT协议)

本篇博客主要围绕EMQX是什么&#xff1f;、能干什么&#xff1f;、怎么用&#xff1f; 三点来进行整理。 1、MQTT协议 1.1、MQTT简介 在了解EMQX前首先了解一下MQTT协议&#xff0c;MQTT 全称为 Message Queuing Telemetry Transport&#xff08;消息队列遥测传输&#xff0…

IP协议及相关技术协议

一、IP基本认识 1. IP的作用 IP在TCP/IP模型中处于网络层&#xff0c;网络层的主要作用是实现主机与主机之间的通信&#xff0c;而IP的作用是在复杂的网络环境中将数据包发送给最终目的主机。 2. IP与MAC的关系 简单而言&#xff0c;MAC的作用是实现“直连”的两个设备之通信…

内存计算研究

近年来&#xff0c;应用数据呈现爆炸式增长&#xff0c;处理器和主存之间的带宽限制成为数据密集型应用的瓶颈。此外&#xff0c;目前流行的一些数据密集型应用&#xff0c;如神经网络应用和图计算应用&#xff0c;数据的局部性差。这会导致处理器片上缓存命中率降低&#xff0…

深究 DevOps 与平台工程的区别

今天&#xff0c;我们将讨论平台工程和 DevOps 的关系。尽管这两个概念有一些共同点&#xff0c;但它们仍然是截然不同的&#xff0c;我们将具体了解它们之间的区别。本文旨在解释当代软件工程中的这两个基本概念。通过实际案例&#xff0c;我们将分别说明这两个方法如何塑造了…