JAVAEE初阶相关内容第十六弹--网络编程

写在前

这一节的内容首先是对十五弹(UDP回显服务器)进行简单的改进,在这基础上开始介绍TCP流套接字编程。

目录

写在前

1.改进回显服务器

1.1完整代码实现

1.2运行输出结果

2.TCP流套接字编程

2.1ServerSocketAPI

2.2SocketAPI

3.TCP版本的回显服务器

3.1代码实现

3.1.1服务器端完整代码

3.1.2客户端完成代码

3.2TCP中的长短连接

3.3解决C10M问题


1.改进回显服务器

上一节中的回显服务器,缺少业务逻辑,这里我们对代码进行改进,实现一个查词典的功能

1.1完整代码实现

这里需要的是需要重写一下process方法!

package network;

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 苏西西
 * Date: 2023-10-19
 * Time: 21:55
 */
//对于DictServer来说,和EchoServer相比大部分东西都是一样的,继承复用原来的代码
//主要是根据请求计算响应这个步骤不太一样
public class UdpDictServer extends UdpEchoServer{
    private Map<String,String> dict = new HashMap<>();
    public UdpDictServer(int port) throws SocketException {
        super(port);
        //给这个dict设置一下内容
        dict.put("cat","小猫");
        dict.put("dog","小狗");
        dict.put("pig","zyt");
        //可以无限多设置

    }
    @Override
    public String process(String request){
        //查词典
        return dict.getOrDefault(request,"查不到!");

    }

    public static void main(String[] args) throws IOException {
        UdpDictServer server = new UdpDictServer(4000);
        server.start();

    }
}

1.2运行输出结果

2.TCP流套接字编程

TCP并不需要一个类来表示“TCP”数据报。

TCP不是以数据报为单位进行传输的,是以字节的方式,流式传输的。

2.1ServerSocketAPI

ServerSocket 专门给服务器使用的Socket对象。

ServerSocket构造方法
方法签名方法说明

ServerSocket(int port)

这里的port指的是服务器要绑定的端口。

创建一个服务端流套接字Socket,并绑定到指定端口。
ServerSocket方法
方法签名方法说明
Socket accept( ) 开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
void close()关闭套接字

这里,accept() 类比于接电话;返回一个服务端Socket对象:接电话后会返回一个Socket对象,通过这个socket对象和客户端进行沟通。

2.2SocketAPI

Socket 既会给客户端使用,也会给服务器使用。

在服务器这边,是由accept返回的;在客户端这边,代码构造的时候指定一个IP和端口号(此处值指的是服务器的IP和端口),有了这个信息就可以和服务器建立连接了。

Socket构造方法
方法签名方法说明
Socket(String host,int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接。
Socket方法
方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址

InputStream getInputStream()

返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

进一步通过Socket对象,获取到内部的流对象,借助流对象来进行发送/接收。

3.TCP版本的回显服务器

3.1代码实现

3.1.1服务器端完整代码

package network;

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 TcpEchoServer {
    private ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        System.out.println("启动服务器!");
        while (true){
            //使用这个clientSocket和具体的客户端进行交流。
            Socket clientSocket = serverSocket.accept();
            processConnection(clientSocket);
        }
    }

    //使用这个放方法来处理一个连接。
    //这一个连接对应到一个客户端,但是这里可能会涉及到多次交互。
    private void processConnection(Socket clientSocket) {
        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 = new Scanner(inputStream);
                if (!scanner.hasNext()){
                    //如果没有下一个说明读完了,客户端关闭了连接
                    System.out.printf("[%s:%d] 客户端下线\n!",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //注意:此处使用的next是一直读取到换行符/空格/其他空白符结束
                //最终返回结果里不包含上述空白符
                String request = scanner.next();

                //2.根据请求构造响应
                String response = process(request);

                //3.返回响应结果
                //对于OutputStream 没有write String这个功能,可以把String里的字节数组拿出来,进行写入;
                //也可以用字符流来转换一下
                PrintWriter printWriter = new PrintWriter(outputStream);
                //此处使用println来写入,让结果中带有一个\n换行,方便对端(接收端)接收解析
                printWriter.println(response);
                //flush用来冲刷缓冲区,保证当前写入的数据确实发送出去了
                printWriter.flush();

                //打印一个日志
                System.out.printf("[%s:%d] req:%s;resp:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),
                        request,response);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //更合适的做法是将close放到finally里面,保证close()一定会被执行到
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String process(String request) {
        return request;
    }

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

}

这里的accept() 效果是“接收连接”,前提是得有客户端来建立连接。客户端在构造Socket对象的时候就会指定服务器的IP和端口,如果没有客户端来连接,此时的accept就会阻塞。


TCP socket里面涉及两种socket对象。

这个代码中,用到了一个clientSocket,此时任意一个客户端连上来都会返回/创建一个Socket对象。(Socket就是文件)每次创建一个clientSocket对象就要占用一个文件描述符表的位置。

因此在使用完毕之后,就需要进行“释放”。

在前面的记录中,socket都没有释放,一方面这些socket生命周期更长(跟随整个程序),另一方面这些socket也不多,固定数量。

但是此处的clientSocket数量多,每个客户端都有一个,生命周期也更短。

3.1.2客户端完成代码

package network;

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;

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 {
    //当前这里的socket既给服务器用,又给客户端用,
    private Socket socket = null;
    public TcpEchoClient(String serverIp,int serverPort) throws IOException {
        //Socket构造方法能够识别点分十进制格式的ip地址,比DatagramPacket更方便
        //new这个对象的同时,就会进行Tcp连接操作
        socket = new Socket(serverIp,serverPort);
    }
    public void start(){
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);

        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            while (true){
                //1.先从键盘上读取用户输入的内容
                System.out.print(" >");
                String request  = scanner.next();
                if(request.equals("exit")){
                    System.out.println("goodbye");
                    break;
                }
                //2.把读到的内容构造成请求,发送给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                //此处加上flush,保证我们的数据确实发送出去了
                printWriter.flush();
                //3.读取服务器的响应,
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();

                //4.把响应的内容给显示到界面上
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

这个对象的构造过程,就是触发TCP建立连接的过程(打电话就开始拨号了),如果客户端没有这个代码,服务器就会在accept这里阻塞,无法产生出clientSocket了。

outputSteam相当于是​对应着一个文件描述符表(socket文件),通过outputStream就可以往这个文件描述符表中写数据。

outputStream自身的方法不便写字符串,把这个流转换一下,用一个PrintWrite对象来表示(对应的文件描述符表还是同一个)

使用PrintWrite写和OutputStream写,是往同一个地方写,只不过写的过程更方便了。

3.2TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接,就决定是短连接还是长连接。

(1)短连接:客户端每次给服务器发消息,先建立连接,发送请求,读取响应,关闭连接。下次再发送,则重新建立加连接。

(2)长连接:客户端,建立连接之后,连接不着急断开,然后再发送请求,读取响应​​​​;若干轮之后,客户端确实短时间内不再需要这个连接了,此时就断开。

服务器这里的开发很少有不用多线程的情况(当然也有用多进程的)。

这里就可以对代码进行改进:

使用多线程版本处理程序,最大的问题就是可能会涉及到频繁的申请释放线程。

在多线程的基础上,使用线程池进行代码编写。

但是如果客户端非常多,而且客户端还迟迟不断开的高压,就回导致机器上会有很多线程。此时就就会提起一种方法:增加机器。但是这就意味着需要增加成本,需要多花钱,这里的问题又被称为是C10M问题。

3.3解决C10M问题

C10K问题:单机处理1W个客户端。

C10M问题,单机处理1kw个客户端(针对多线程的版本,最大的问题就是机器承担不了这么大的线程开销,是否有办法一个线程,处理很多个客户端连接?)

IO多路复用/IO多路转接

给这个线程安排个集合,这个集合就放了一堆连接,线程就负责监听这个集合,这里的哪个连接有数据来,线程就处理哪个连接。虽然连接有很多,但是这些连接的请求并非严格意义的同时,总还是有先后的。

在操作系统中提供了一些原生的API select、poll、epoll 在Java中提供了一组NIO 这样 类,就封装了上述多路复用的API。

这一节和前一节的内容主要介绍的就是Udp和TcpSocket编程,需要注意的是,关于有无连接、面向字节流还是数据报以及全双工与否,这些特点在代码中都是有体现的。关于时候是可靠传输这一特点是隐藏在TCP背后,从代码的角度是感受不到的。TCP诞生的初心也就是为了解决可靠传输的问题。

下一部分将介绍网络原理知识的相关内容。

继续加油哦!

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

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

相关文章

小知识(6) el-table表格选中行和回显行(vue3)

el-table表格选中行和回显行 官方文档说明 https://element-plus.org/zh-CN/component/table.html#table-%E6%96%B9%E6%B3%95 环境&#xff1a;vue3element-plus 选中行selection <el-table ref"baseTableRef" row-key"id" border :selection"tr…

【OpenCV实现平滑图像形态学变化】

文章目录 概要目标腐蚀膨胀开运算结构元素&#xff08;内核&#xff09;小结 概要 形态学变化是一组简单的图像操作&#xff0c;主要用于处理二值图像&#xff0c;即只包含黑和白两种颜色的图像。这些操作通常需要两个输入&#xff0c;原始图像和一个内核&#xff08;kernel&a…

第11期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

CloudQuery + StarRocks:打造高效、安全的数据库管控新模式

随着技术的迅速发展&#xff0c;各种多元化的数据库产品应运而生&#xff0c;它们不仅类型众多&#xff0c;而且形式各异&#xff0c;国产化数据库千余套&#xff0c;开源数据库百余套 OceanBase 、PolarDB 、StarRocks…还有一些像 Oracle、MySQL 这些传统数据库。这些数据库产…

flutter开发实战-hero实现图片预览功能extend_image

flutter开发实战-hero实现图片预览功能extend_image 在开发中&#xff0c;经常遇到需要图片预览&#xff0c;当feed中点击一个图片&#xff0c;开启预览&#xff0c;多个图片可以左右切换swiper&#xff0c;双击图片及手势进行缩放功能。 这个主要实现使用extend_image插件。在…

达梦:开启sql日志记录

前言 开启sql日志记录&#xff0c;可协助排查定位数据库问题。生产开启会有一定的性能消耗&#xff0c;建议打开 SQL 日志异步刷盘功能 1.配置sqllog.ini文件 sqllog.ini 用于 SQL 日志的配置&#xff0c;当且仅当 INI 参数 SVR_LOG1 时使用。 运行中的数据库实例&#xff0c;可…

Go学习第十一章——协程goroutine与管道channel

Go协程goroutine与管道channel 1 协程goroutine1.1 基本介绍1.2 快速入门1.3 调度模型&#xff1a;MPG模式介绍1.4 设置cpu数1.5 协程资源竞争问题1.6 解决协程并发方案 2 管道channel2.1 基本介绍2.2 快速入门2.3 管道的关闭和遍历2.4 管道和协程的结合2.5 声明 只读/只写 的管…

GZ035 5G组网与运维赛题第2套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项(高职组) 赛题第2套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通(35分) 子任务1:5G公共网络部署与调试(15分) 子任务2:5G室内与室外站点建设(20分) 竞赛模块2--5G公共网络运维与优化(…

Codeforces Round 905 (Div. 3)ABCDEF

Codeforces Round 905 (Div. 3) 目录 A. Morning题意思路核心代码 B. Chemistry题意思路核心代码 C. Raspberries题意思路核心代码 D. In Love题意思路核心代码 E. Look Back题意思路核心代码 A. Morning 题意 从一开始&#xff0c;每一次操作可以选择当前的数字打印或者是移…

Vue3 + Tsx 集成 ace-editor编辑器

Ace Editor介绍 Ace Editor&#xff08;全名&#xff1a;Ajax.org Cloud9 Editor&#xff09;是一个开源的代码编辑器&#xff0c;旨在提供强大的代码编辑功能&#xff0c;通常用于构建基于Web的代码编辑应用程序。它最初由Cloud9 IDE开发&#xff0c;现在由开源社区维护。 主…

C++ 左值、右值、左值引用以及右值引用

一、左值和右值 将亡值 1.左值 左值是一个表示数据的表达式&#xff0c;比如&#xff1a;变量名、解引用的指针变量。一般地&#xff0c;我们可以获取它的地址和对它赋值&#xff0c;但被 const 修饰后的左值&#xff08;常性&#xff09;&#xff0c;不能给它赋值&#xff0…

【安装tensorflow-CPU版本】

一、安装目的二、安装过程三、总结 一、安装目的 使自己的jupyter能用tensorflow 二、安装过程 首先打开anaconda prompt 接着输入conda list 查看自己是否安装了tensorflow 在 Python 中使用 pip 工具来升级 pip 自身并指定了使用清华大学的镜像源进行安装 python -m pip …

手写Vue渲染器render函数

使用js对象来描述UI更加的灵活。“这种对象”在vue框架中被称为虚拟DOM&#xff0c;渲染函数内部可以创建虚拟DOM&#xff0c;然后vue.js可以将其内容进行渲染。 1.渲染器的介绍 渲染器的作用就是把虚拟DOM渲染为真实DOM 思考下&#xff0c;我们有一个虚拟 DOM&#xff0c;如…

K8s概念汇总-笔记

目录 1.Master 1.1在Master上运⾏着以下关键进程 2.什么是Node? 1.2在每个Node上都运⾏着以下关键进程 3.什么是 Pod ? 4. 什么是Label &#xff1f; 5.Replication Controller 6.Deployment 6.1Deployment的典型场景&#xff1a; 7.Horizontal Pod Autoscaler TODO…

【计算机毕设小程序案例】基于SpringBoot的小演员招募小程序

前言&#xff1a;我是IT源码社&#xff0c;从事计算机开发行业数年&#xff0c;专注Java领域&#xff0c;专业提供程序设计开发、源码分享、技术指导讲解、定制和毕业设计服务 &#x1f449;IT源码社-SpringBoot优质案例推荐&#x1f448; &#x1f449;IT源码社-小程序优质案例…

Linux mkdir命令:创建目录(文件夹)

mkdir 命令&#xff0c;是 make directories 的缩写&#xff0c;用于创建新目录&#xff0c;此命令所有用户都可以使用。mkdir 命令的基本格式为&#xff1a; [rootlocalhost ~]# mkdir [-mp] 目录名 -m 选项用于手动配置所创建目录的权限&#xff0c;而不再使用默认权限。 -p…

[ubuntu系统下的文本编辑器nano,vim,gedit,文件使用,以及版本更新问题]

文本编辑器概要 在Ubuntu系统下&#xff0c;有许多文本编辑器可供选择&#xff0c;每个编辑器都有其独特的特性和用途。以下是一些常见的文本编辑器&#xff1a; Gedit&#xff1a; 这是Ubuntu默认的文本编辑器&#xff0c;它简单易用&#xff0c;适合基本的文本编辑任务。 安…

银河麒麟v10x86或者arm离线安装服务

银河麒麟v10x86或者arm离线安装服务 最近有个项目&#xff0c;甲方的服务器用的全是国产化服务器银河麒麟&#xff0c;架构是x86的然后也无法连接外网&#xff0c;需要离线安装服务正常思路就是找到离线安装的包&#xff0c;然后拷贝到现场的服务器中进行安装所以问题就在于如…

墨西哥专线相关问题快问快答

随着全球贸易的不断发展&#xff0c;越来越多的企业在寻求更便捷、高效的物流解决方案。墨西哥专线作为一种跨境物流方式&#xff0c;受到了越来越多企业的关注。本文将为您解答关于墨西哥专线的相关问题&#xff0c;帮助您更好地了解和运用这一物流方式。 一、墨西哥专线是什么…

Prompt设计与大语言模型微调

本文主要介绍了Prompt设计、大语言模型SFT和LLM在手机天猫AI导购助理项目应用。 ChatGPT基本原理 “会说话的AI”&#xff0c;“智能体” 简单概括成以下几个步骤&#xff1a; 预处理文本&#xff1a;ChatGPT的输入文本需要进行预处理。输入编码&#xff1a;ChatGPT将经过预处理…