网络编程(TCP+UDP)(3)

news2024/12/29 16:44:26

1)咱们之前所说的网络分层就是因为说如果说使用一个协议太复杂了,那么我们就需要把这个协议分层,每一个协议都会简单一些,灵活替换也更方便;

2)咱们现在需要实现一个网络计算器;

 

1)上图是模拟一个计算器服务器和客户端的工作流程,在实际开发中非常常见;

2)进行运算,要吃CPU,进行传输,吃的是网络宽带;

 

客户端给服务器发送的请求:
字符串:第一个操作数,第二个操作数,运算符;
服务器给客户端返回的响应:计算结果
下面是实现运算服务器的代码
所谓的自定义协议,一定是在开发之前,就约定好的,开发过程中,就要让客户端和服务器,都能够严格遵守协议约定好的格式,直接使用文本+分隔符,假设传输的请求和响应中,各自有几十个字段,况且有些字段是可选的;

1)应用层会使用自己的现成的http协议,同时我们还可以自己约定一个应用层协议,上面的不孕不育搜索的结果后一定先传回的是广告(广告放送的钱多)既要考虑相关性,又要考虑广告商的出价

2)自己进行运算, 要使用大量的CPU资源,本是可以在客户端进行数据的运算的,但是站在占用CPU的资源角度上面考虑,还是搞一个服务器;

 

这是咱们的客户端的代码:

package demo2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class TcpRequestRequest {
    public String serverIP;
    public int serverPort;
    private Socket socket=null;
    public TcpRequestRequest(String serverIP, int serverPort) throws IOException {
        this.serverIP=serverIP;
        this.serverPort=serverPort;
        this.socket=new Socket(serverIP,serverPort);
    }
    public void start(){
        System.out.println("客户端开始进行启动");
        try(InputStream inputStream=socket.getInputStream()) {
            try (OutputStream outputStream = socket.getOutputStream()) {
        while(true){
//1.先进行输入数据
            Scanner scanner=new Scanner(System.in);
            System.out.println("请输入第一个操作数");
            String s1=scanner.nextLine();
            System.out.println("请输入操作符");
            String s2=scanner.nextLine();
            System.out.println("请输入第二个操作数");
            String s3=scanner.nextLine();
            String request=s1+"B"+s2+"B"+s3;
//2.发送数据

        //2.1再进行发送数据
        PrintStream printStream = new PrintStream(outputStream);
        printStream.println(request);
        printStream.flush();
        //2.2再进行接受服务器的响应
        Scanner result = new Scanner(inputStream);
        String response = result.nextLine();
        //2.3打印在控制台上面
        System.out.println(response);
        System.out.println("是否接下来要继续进行计算?");
        String next = scanner.nextLine();
        if (next.equals("N")) {
            System.out.println("即将退出客户端");
            break;
        }
    }
}
} catch (IOException e) {
    e.printStackTrace();
}
    }
    public static void main(String[] args) throws IOException {
       TcpRequestRequest request=new TcpRequestRequest("127.0.0.1",9999);
       request.start();
    }
}

下面这个是咱们的服务器的代码:

package demo2;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpClimentServer {
    public int serverPort;
    private ServerSocket listensocket=null;
    public TcpClimentServer(int serverPort) throws IOException {
        this.serverPort=serverPort;
        this.listensocket=new ServerSocket(serverPort);
    }
    public void start() throws IOException {
        System.out.println("开始启动我们的服务器");
        while(true){
            Socket socket=listensocket.accept();
            System.out.printf("客户端的IP地址是%s,客户端的端口号是%d",socket.getInetAddress(),socket.getPort());
                procession(socket);
        }
    }
    private void procession(Socket socket) {
            try(InputStream inputStream=socket.getInputStream()) {
                try(OutputStream outputStream=socket.getOutputStream()){
                    while(true){
//1.我们先进行读取客户端的请求
    Scanner scanner=new Scanner(inputStream);
    String request=scanner.nextLine();
//2.根据请求进行处理业务逻辑
     String response=Process(request);
//3.根据业务逻辑来进行计算响应
    PrintWriter writer=new PrintWriter(outputStream);
    writer.println(response);
    writer.flush();
//4.显示计算结果
   System.out.printf("客户端的请求是%s 服务器返回的响应是%s",request,response);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
                e.printStackTrace();
            }
    }
    public String Process(String request){
        //1.判断数据是否为空
        if(request==null||request.equals("")){
            return "当前传输数据有误,什么也没有传输过来";
        }
        //2.按照标点符号来进行分割
        String[] strings=request.split("B");
        if(strings.length!=3){
            return "您当前数据所进行输入的格式是存在错误的";
        }
        //3.取出里面的数字和符号来进行运算
        int result=0;
        Integer s1=Integer.parseInt(strings[0]);
        Integer s2=Integer.parseInt(strings[2]);
        String operate=strings[1];
        if(operate.equals("+")){
            result=s1+s2;
        }else if(operate.equals("-")){
            result=s1-s2;
        }else if(operate.equals("*")){
            result=s1*s2;
        }else{
            result=s1/s2;
        }
        return String.valueOf(result);
    }

    public static void main(String[] args) throws IOException {
        TcpClimentServer server=new TcpClimentServer(9999);
        server.start();
    }
}

一:应用层协议:这个协议,实际上是和程序员打交道最多的协议了

1)其它四层都是操作系统,驱动,硬件实现好了的,咱们是不需要管

2)应用层:当我们继续进行写应用程序的时候,尤其是应用程序负责网络通信的时候,往往是需要自定义应用层协议的,我们要根据需求,明确要进行传递的信息,我们还要明确传递的数据格式;

3)在我们的应用层这里面,最重要的事情,就是说自己实现一个应用层协议,比较简单也是在工作中经常做的事情;

FTP,SSH,TELNET,DNS(域名解析的协议)

1)使用现成的应用层协议来进行开发

2)程序员自己约定一个协议,来进行开发,只要客户端和服务器自己进行开发的;例如我在网页上面给服务器发送了一个请求,请求中包含着一个链接,然后服务器给浏览器返回了一个响应,这个响应就是一个网页;在这个过程中,使用的协议就是HTTP协议这时我们就可以
使用自定义协议,来约定好请求是什么格式,响应是什么格式,客户端和服务器就可以按照这样的约定来进行开发了;

3)总而言之,自定义协议,其实是一个很简单的事情,我们只需要约定请求和响应的详细格式即可,越详细越好,要把各种细节都能够交代到,能够更好的表示当前的信息,咱们也可以自己来约定好格式,也可以基于xml,Json来约定数据格式,甚至还可以通过一些其他的二进制的方式来进行约定数据

回显服务器:无法支持多个客户端并发执行
多线程回显服务器:针对每一个连接,每一个客户端,创建一个线程;
线程池回显服务器:避免频繁创建销毁线程

1)例如我们要提供一个新的需求,要求在外卖软件的首页,显示一个优惠活动,用户参与活动,就可以参加抢红包的操作;
2)客户端:修改界面,可以显示优惠活动的详情;
3)服务器:修改后台逻辑,针对啥样的用户可以参加优惠,具体怎么做可以领到红包,红包金额是多少;
4)当我们的客户端启动的时候,就要向服务器进行查询,当前用户是否可以进行参加活动,服务器就要返回是或者否
5)这个时候在开发功能之前,就要约定好,客户端发的查询请求的功能是怎么样的(带有用户的身份信息),在约定好服务器返回的响应是什么样的(1表示可以表示能参与,0表示不能参与,true表示能参与,false表示不能参与)

1)假设我们现在自己实现一个功能叫做获取用户的订单历史(这个订单历史在数据库里面,我们要从服务器操作数据库来拿,这样的功能就会涉及到前后端的一个交互过程),咱们的前端和后端就是通过网络来进行交互的;

2)在这个交互的过程中我们就需要约定好,前端发送什么样子的数据,后端要返回什么样子的数据,本质上来说就是在进行规划请求和响应之间的信息,以及传数据的格式;

咱们进行查历史订单,查找谁的订单,以及查找哪一个时间段的订单,以及数据太多,你要不要截取一部分;

1)我们前端发送的请求类似于:

{

    用户的ID,

    查询的起始时间,

    查询的结束时间,

    显示的条数;

}

2)我们后端返回的响应:

2.1)查询是否成功?如果失败,原因都有哪些?

2.2)返回一个订单数组:

数组里面的每一个元素:

{

    商品名,

    商品单价,

    商品数量,

    店铺名称,

    总的金额

}

像上面的过程就相当于是设计应用层协议,咱们设计应用层协议,本质上就是做两个工作

1)明确我们要传输的信息

2)明确数据的组织格式,请求的格式是啥样子的,响应是啥样子的

1)当前我们只是给出了一种可能的格式,此处我们这里面的数据的格式,也是可以进行随心所欲的约定的

2)相比之下我们只需要让我们的客户端和服务器都按照一定的格式来进行交互就可以了;

3)上面的这一种设计十分不好,咱们的输出传输效率比较低,这也就说明了是运行效率低;

4)咱们的这个代码可读性也不好,本身开发效率也比较低,正因为如此,大佬们发明了一些比较好的协议的模板,让我们向上套;

XML,JSON,protobuffer,前两种可读性比较好,但是运行效率不高,咱们的第三种可读性不好,但是运行效率比较高;

 

                          ——————————————————————————————————————————

1)咱们的XML就是一种老牌的数据格式了,虽然现在也在用,但是越来越少了,咱们的这些标签本身占据的空间已经超过了数据本身所占用的空间,也就说引入了更多的辅助信息,我们程序的效率就会受到很大的影响,对于一个服务器程序来说最贵硬件资源就是网络带宽,对于XML来说,虽然提高了可读性,但是又引入了太多的辅助信息,比如说标签名字,这些辅助信息所进行占用的空间甚至已经超过了我们数据本身的一个空间,因为在XML里面要经常表示这些辅助信息,在传输相同的网络数据的时候,这就会导致传输相同数目的请求的时候,占用的网络带宽是更高的,如果说带宽固定,那么相同时间内能够传输的请求数目就是更少的,所以说我们程序的效率就会收到很大的影响;

2)咱们现在看这个XML标签,标签名就是key,标签值就是value,就构成了一个键值对,通过这些标签,就更好的体现了我们数据的可读性,尤其是哪一个部分代表是什么意思,非常一目了然,咱们的一个服务器程序最贵的硬件资源就是网络带宽,因此我们的XML现在已经很少作为应用层协议的设计模板了,我们现在使用XML主要是为了做配置文件;

3)JSON的传输效率虽然要比XML要好一些,但是还是要多进行传递一些冗余信息,就是Key的名字,但是Json格式的数据也是有非常大的缺点,尤其是在进行表示数组的时候,这里面的Key是在不断重复的这也会占用大量的带宽资源但是咱们的JSON是一种最重要的设计模式

4)因此咱们的protobuffer应运而生,这是一种二进制格式的数据,在这种格式的数据里面,不会再包含上面的key的名字了,而是通过顺序或者是一些特殊的符号,来进行区分每一个字段的含义,同时在通过一个IDL文件,来进行描述这个数据格式(来进行描述每一个部分是啥意思),IDL只是起到一个辅助性开发的效果,并不会真正的进行传输,传输的只是二进制的纯粹的数据,似乎传输效率高了,但是开发效率低,可能是把对应的文字换成了对应的二进制数据,咱们通过二进制的数据对这里面的内容重新地进行了编排,甚至有可能做出一些数据压缩,只进行传递一些必要的信息,传输效率会更高,但是这些数据肉眼难以观察;

5)开发效率低,是包含了开发和调试,调试很不方便,如果说咱们的线上环境出现了问题,如果说咱们用JSON,出问题的请求和响应,一目了然(一看就知道哪里出现了问题),如果使用protobuffer,二进制的数据,根本没法用肉眼看,我们就需要自己开发一个专门的程序来进行解析这里面的数据,来进行分析这里面的问题,这就会比较麻烦;

咱们广论开发:咱们的protobuffer里面有IDL,这个就是说咱们的protobuffer约定的一种文件格式,类似于C语言结构体的写法

class message Response{
     bool ok=1;
     string reason=2;
     repeated Data data=3;
}

咱们来进行设计应用层协议,是一件非常普遍的事情,也是一种并不复杂的事情;

设计应用层协议,我们要做的工作就是说:

1)我们要根据需求,明确传输的信息

2)我们要进行明确数据传输的格式,我们要参考现有模板,比如说JSON,XML,protobuffer

1)上面写错了 ,把上面的等于号全部换成,所以说json格式的字符串,是一个键值对结构,键和值之间使用:分割,键与键之间,使用,来进行分割,最外面加上一个{};

2)咱们的Json格式的数据要求key一定要是字符串因此这里面的key值一定是字符串类型,咱们的值可以是很多种类型因此这里面的key的引号是可以省略的,但是如果说key包含了一些特殊的符号比如说像空格,或者一些特殊字符,那么一定要加上引号,值是可以是很多种的,比如说数字,字符串,布尔类型,数组,另一个JSON;

3)在咱们的JSON里面想要表示一个字符串,我们使用单引号,双引号都是可以的,况且说在我们的最后一个键值对后面,可以有逗号也可以没有逗号,相比于咱们的XML,咱们的JSON同样是可以保证可读性的,同时又没有像XML那样繁琐,占用的带宽更少一些;

{                        {      
  num1:10,                   result=30,  
  num2:20,               }  
operator:"+"             
}

端口号的用途:标识一个进程,就可以区分当前数据要交给哪个进程来处理          

端口号:端口号是一个整数,用来区分进程;但是PID(用于进程的身份标识)也是一个整数(PCB中的一个特性),用来区分进程,为什么在网络编程中,不直接使用PID,而是直接用到端口号这样的概念呢?

端口号是固定不变的,端口号我们可以手动指定,但是PID每次进程启动之后都会发生改变,这是系统自动分配的,我们无法控制;

1)例如客户端要连接一个服务器,客户端就要先知道服务器的IP与PID,一旦服务器进行重启,PID就会发生改变
中国移动:10086--类似于端口号,这是固定不变的
转换人工:工号xxx为您服务
他就类似于PID,每次接通电话可能都不一样
同样的,两个进程无法绑定到同一个端口号
2)系统提供的原生的socket API其中有一个方法就叫做bind,功能就是把端口号和一个socket关联起来,一个进程是可以绑定多个端口的

1)例如在开发服务器的时候,首先会让服务器提供一个业务端口,通过这个端口,就可以提供一些广告搜索的服务(上游服务器,就可以通过这个端口来获取到广告数据)
2)其次服务器还会提供一个调试端口,在服务器运行过程中,其实会涉及到很多很多的数据,有时候为了定位一些问题,就需要查看一些内存中的数据,通过这些调试端口给服务器发送一些调试请求,于是服务器解耦可以返回一些对应的结果;

3)直接拿调试器打一些断点可以吗?
如果拿调试器来进行断住程序,此时整个进程就会处于一种阻塞的状态的,这就意味着这个服务器是无法响应正常的业务请求的

4)咱们的很多网络服务是进行使用非常常用,非常广泛的服务,为了更好地进行管理,我们就给这些服务分配一些专门的端口号

80---->http服务器

443---->https服务器

23 ------>ssh

23------>ftp服务器

咱们自己的部署http服务器,就可以把他绑到80,也可以把它绑到其他转口

 

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

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

相关文章

第01天-Java数据结构和算法

001_几个经典的算法面试题(1)暴力匹配不推荐KMP算法分治算法002_几个经典的算法面试题(2)回溯算法图的深度优先遍历算法(DFS) 贪心算法优化003_内容介绍和授课方式数据结构和算法的重要性课程亮点和授课方式004_数据结构和算法的关系005_编程中实际遇到的几个问题Java代码小结…

机器学习实战(第二版)读书笔记(3)——膨胀卷积,WaveNet

一、基础知识 对于一个卷积层,如果希望增加输出单元的感受野,一般可以通过三种方式实现: 增加卷积核的大小增加层数(比如两层3 3 的卷积可以近似一层5 5 卷积的效果)在卷积之前进行池化操作 其中第1,2种…

推荐5个很牛的开源项目

大家伙们年过了哈,该收拾收拾心情上班了。 不知道大家有没有这种感觉,年纪越大,越觉得年过得快。感觉好像才刚开始,马上初五了,初六送完穷鬼,初七送自己出来上班了(没有哭)。 不过…

Python编写的词频统计工具的使用说明

一、工具下载 https://download.csdn.net/download/huangbangqing12/87400984 二、工具使用方式 目录文件如下所示: 请先在word.txt文件里放入目标长尾词,一行一个: 文件-另存为: 选择utf-8编码并直接保存替换原文件&#xff1…

二叉树的层次遍历

文章目录二叉树的层次遍历二叉树的层次遍历107. 二叉树的层序遍历 II199. 二叉树的右视图637.二叉树的层平均值429. N 叉树的层序遍历515.在每个树行中找最大值116. 填充每个节点的下一个右侧节点指针填充每个节点的下一个右侧节点指针II104.二叉树的最大深度二叉树的最小深度二…

ESPnet

文章目录关于 ESPnet安装配置运行 yesno关于 ESPnet github: https://github.com/espnet/espnet ESPnet is an end-to-end speech processing toolkit covering end-to-end speech recognition, text-to-speech, speech translation, speech enhancement, speaker …

机器自动翻译古文拼音 - 十大宋词 - 声声慢 寻寻觅觅 李清照

声声慢寻寻觅觅 宋李清照 寻寻觅觅,冷冷清清,凄凄惨惨戚戚。 乍暖还寒时候,最难将息。 三杯两盏淡酒,怎敌他、晚来风急。 雁过也,最伤心,却是旧时相识。 满地黄花堆积,憔悴损,如今…

Web 应用程序——我的心理备忘单

介绍本文是“持续交付:HTML 到 Kubernetes”的一部分。虽然我迫不及待地想深入了解分布式系统的细节,但我发现自己处于一个不愉快的境地:我认为最好从前端开始写。那是因为网络应用程序是当今的标准。在多个云中部署的 ArgoCD-Kubernetes 集群…

商业智能BI,大数据时代的新趋势

根据IDC预测,2025年时中国产生的数据量预计将达48.6ZB,在全球中的比例为27.8%。在未来,数据会是构建现代社会的基本要素,也是社会的基本建设。这也不禁让我想起了最近新公布的《关于构建数据基础制度更好发挥数据要素作用的意见》…

高并发下如何保证接口的幂等性?

一、什么是幂等? 看一下维基百科怎么说的: 幂等性:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。 二、使用幂等的场景 1、前端重复提交 用户注册,用户创建商品等操作&#…

使用这个工具,本地调试UI再也不用怕了

前言:在我们日常使用中,很多场景都会用到UI自动化,通用的都是PythonSelenium的方式。今天介绍一种,不用通过代码,直接通过页面可视化配置的方式,就可以完成我们想要的自动化场景。话不多说,正片…

ElasticSearch - 结果处理

目录 结果处理-排序 结果处理-分页 结果处理-高亮 结果处理-排序 elasticsearch默认是根据相关度算分(_score)来排序,但是也支持自定义方式对搜索结果排序可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等普通字段排序keyword、数…

pytorch图像分类全流程(五)--图像分类算法精度评估指标

本次我们来学习图像分类算法精度的各种评估指标:precision、recall、accuracy、f1-score、AP、AUC。 首先我们来学一个很重要的概念,混淆矩阵: 1.精确率(Precision): 指的是所有被判定为正类(TPFP)中&…

8-Arm PEG-Succinamide Acid,8-Arm PEG-SAA,八臂-聚乙二醇-丁二酸酰胺供应

英文名称:8-Arm PEG-SAA,8-Arm PEG-Succinamide Acid 中文名称:八臂-聚乙二醇-丁二酸酰胺 8-臂PEG-SAA是一种多臂PEG衍生物,在连接到一个六甘油核心的八个臂的每个末端具有羧基。PEG和丁二酰胺酸COOH基团之间存在C3酰胺键。PEG酸…

HTML中引入CSS样式的第一种方式:内联定义方式

<!-- HTML中引入CSS样式的第一种方式&#xff1a;内联定义方式 语法格式&#xff1a; <标签 style"样式名:样式值;样式名:样式值;样式名:样式值;..."></标签> --> <!DOCTYPE html> <html> <head> …

操作系统(day01)

文章目录操作系统的功能和目标1.作为系统资源的管理者&#xff08;从中间往两边看&#xff09;2.作为用户和计算机硬件之间的接口&#xff08;从下往上看&#xff09;操作系统的四大特征共享虚拟异步操作系统的发展与分类手工操作阶段批处理阶段--多道批处理系统分时操作系统实…

基于蜣螂算法的极限学习机(ELM)回归预测-附代码

基于蜣螂算法的极限学习机(ELM)回归预测 文章目录基于蜣螂算法的极限学习机(ELM)回归预测1.极限学习机原理概述2.ELM学习算法3.回归问题数据处理4.基于蜣螂算法优化的ELM5.测试结果6.参考文献7.Matlab代码摘要&#xff1a;本文利用蜣螂算法对极限学习机进行优化&#xff0c;并用…

QT入门与基础控件

目录 一、QT入门 1.1QT简介 1.2经典应用 1.3工程搭建 1.3.1按钮 1.3.2行编辑框 1.3.3简单确定位置 1.4信号与槽机制 二、布局管理器 2.1布局管理器 2.2输出控件 2.3输入控件 2.4按钮 2.5容器 2.5.1Group Box 2.5.2Ccroll Area 2.5.3Tool Box 2.5.4 Tab Wid…

射频脉冲频谱及退敏效应简述

当使用频谱仪测试射频脉冲信号的频谱时&#xff0c;设置不同的RBW可以得到不同的结果&#xff0c;有连续的包络谱和离散的线状谱之分。针对简单的射频脉冲而言&#xff0c;脉冲退敏效应是指&#xff0c;当显示线状谱时&#xff0c;中心载波的幅度将低于脉内平均功率&#xff0c…

网络工程师必修课主流两种方式实现不同VLAN间通信

我们知道默认不同VLAN间数据时不能通信的,想要实现不同VLAN间通信常用的有两种方式: 一、通过三层交换路由功能实现不同VLAN之间的通信 二、通过单臂路由实现不同VLAN之间的通信 1.通过三层SVI虚接口配置路由实现通信: 交换机A的配置 vlan batch 20 30 //创建VLAN20 V…