回显服务器

.

在这里插入图片描述

写一个应用程序,让这个程序可以使用网络通信,这里就需要调用传输层提供的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执行结束的时候,就会得到一个装满内容的DatagramPacketsocket.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","男孩");}@Overridepublic 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() {@Overridepublic 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.读取请求并解析,为了方便读取,直接使用ScannerScanner 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,会很占用内存,因此,需要确保及时被closeclientSocket.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多路复用)

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

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

相关文章

JZ65 不用加减乘除做加法(,^)

一&#xff1a;题目 二&#xff1a;思路 三&#xff1a;代码 int Add(int num1, int num2 ) {//ab (a ^ b)(a&b<<1)//num2 0 时表示没有进位了while(num2){int tmp num1;//得到不进位的数据num1 num2 ^ tmp;//得到进位的数据num2 (tmp&num2)<<1;}ret…

docker 启动镜像命令

Docker 的启动命令用于启动 Docker 容器。这些命令可以从基本的 docker run 命令扩展到包括多个选项和参数&#xff0c;以满足不同的需求。以下是一些常用的 Docker 启动命令和选项的示例&#xff1a; 启动一个新容器&#xff1a; docker run [OPTIONS] IMAGE [COMMAND] [ARG..…

已解决Application run failed org.springframework.beans.factory.BeanNot

问题原因&#xff1a;SpringBoot的版本与mybiats-puls版本不对应且&#xff0c;spring自带的mybiats与mybiats-puls版本不对应 这里我用的是3.2.2版本的SpringBoot&#xff0c;之前mybiats-puls版本是3.5.3.1有所不同。 问题&#xff1a;版本对不上 解决办法&#xff1a;完整…

STM32_ESP8266 连接阿里云 操作图解

一、烧录MQTT固件 ESP8266出厂时&#xff0c;默认是&#xff1a;AT固件。连接阿里云需要&#xff1a;MQTT固件。 因此&#xff0c;我们需要给8266重新烧录 MQTT固件。 针对“魔女开发板&#xff0c;ESP8266模块烧录MQTT固件&#xff0c;图解教程如下&#xff1a; ESP8266 烧录 …

JVM原理学习

一.栈上的数据存储P95 二.堆上的数据存储 标记字段 指针压缩(节省空间 内存对齐(提高CPU缓存行效率 字段重排列方便内存对齐 类排在基本类型之后 三.JIT实时编译 优化手段 C2编译器&#xff0c;直接将循环相加求和优化为乘法。 方法内联 逃逸分析 四.G1垃圾回收器原理 年轻代…

linux联网yum安装docker

1. 安装依赖包 yum install -y yum-utils device-mapper-persistent-data lvm22.设置阿里云镜像源 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo3.安装docker yum install -y docker-ce4.设置开机自启 #启动docker命…

CV论文--2024.2.19

1、Self-Play Fine-Tuning of Diffusion Models for Text-to-Image Generation 中文标题&#xff1a;自我对弈微调扩散模型&#xff0c;用于文本到图像生成 简介&#xff1a;在生成人工智能&#xff08;GenAI&#xff09;领域&#xff0c;微调扩散模型仍然是一个未被充分探索的…

华为---RSTP(二)---RSTP基本配置示例

目录 1. 示例要求 2. 网络拓扑图 3. 配置命令 4. 测试终端连通性 5. RSTP基本配置 5.1 启用STP 5.2 修改生成树协议模式为RSTP 5.3 配置根交换机和次根交换机 5.4 设置边缘端口 6. 指定端口切换为备份端口 7. 测试验证网络 1. 示例要求 为防止网络出现环路&#xf…

相机图像质量研究(40)常见问题总结:显示器对成像的影响--画面泛白

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

可视化视频监控平台EasyCVR如何配置服务参数以免getbaseconfig接口信息泄露?

可视化云监控平台/安防视频监控系统EasyCVR视频综合管理平台&#xff0c;采用了开放式的网络结构&#xff0c;平台支持高清视频的接入和传输、分发&#xff0c;可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集…

mysql同类型的多行变成一行value1和value2不同的列

关键字 row_number() over (partition by) 例如&#xff0c;下面的数据&#xff0c; 这是按照name分组后&#xff0c;展示property值。 我们想得到这样的值; 第一步&#xff1a;将每一组的property标上序号 select name,property,row_number() over (partition by name order…

Easyx的学习1

使用easys的相关函数需要包含头文件#include<easyx.h>或#include<graphics.h>&#xff08;#include<graphics.h>包含了<easyx.h>和一些不推荐使用的函数&#xff09; 目录 窗口创建背景颜色 基本图形绘制 1.点 2.线 3.矩形 圆角矩形 4. 圆形 椭圆…