【Java网络编程】 三

本文主要介绍了TCP版本的回显服务器的编写。

一.TCP版本回显服务器

1.服务器

服务器的实现流程

1.接收请求并解析

2.根据请求计算出响应(业务流程)

3.把响应返回给客户端

代码:

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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** Tcp版本的回显服务器** 服务器*/public class TcpEchoServer {private ServerSocket serverSocket=null;//使用线程池:此处不应该创建固定线程数目的线程池private ExecutorService service= Executors.newCachedThreadPool();public TcpEchoServer(int port) throws IOException {serverSocket=new ServerSocket(port);}//这个操作会绑定端口public void start() throws IOException {System.out.println("服务器启动");while(true){//从内核中的连接获取到应用程序中/**** accept是把内核中已经建立好的连接,给拿到应用程序中,但是这里的返回值并非是* 一个connection对象,而只是一个socket对象,这个socket对象就像一个耳麦* 可以说话,也可以听到对方的声音*/Socket clientSocket=serverSocket.accept();//单个线程,不方便完成这里的一边拉客,一边介绍;就需要多线程//多线程负责拉客//每次有一个新的客户端,都创建一个新的线程去服务//            Thread t=new Thread(()->{
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//
//            });
//            t.start();//使用线程池也可以解决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]客户端下线 \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}/***这里暗含了一个约定,客户端发过来的请求* 得是文本数据,同时还要包含空白符*/String request=scanner.next();//next一直读到空白符结束(换行,回车,空格,制表符,等)//2.根据请求计算响应String response=process(request);//3.把响应写给客户端/**用printWriter把outputstream包裹一下,方便进行收发数据*/PrintWriter writer=new PrintWriter(outputStream);/*** 使用printWriter的println方法,把响应写给客户端,结尾\n,* 是为了方便客户端读取响应,使用scanner.next读取*/writer.println(response);/*** 还需要加一个刷新缓冲区操作* io操作比较有开销,相比于访问内存,进行io次数越多,程序的速度就越慢** 作为一块内存作为缓冲区,写数据的时候,先写到缓冲区里* 存一波数据,统一进行io* printwriter内置了缓冲区* 手动刷新,确保这里的数据是真的通过网卡发出去了,而不是残留在缓冲区里** 加上flush是更稳妥的做法。*/writer.flush();//打印日志System.out.printf("[%s:%d] rep:%s , resp:%s \n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,request);}} catch (IOException e) {e.printStackTrace();}finally {/*** socek有很多,每来一个连接,就会有一个连接*///在finally中加上close操作,确保当前socket及时关闭。clientSocket.close();}}public String process(String request){return request;}public static void main(String[] args) throws IOException {TcpEchoServer server=new TcpEchoServer(9090);server.start();}}

说明

1.循环之后,服务器要做的事情不是读取客户端的请求,而是先处理客户端的连接,因为TCP是面向连接的。

2.一个服务器中,要对应很对客户端,服务器内核中有很多客户端连接。虽然内核中连接很多,但是应用程序还是要一个一个的处理。

我们可以把内核中的连接看成 待办事项, 待办事项在队列中,应用程序需要一个一个完成这些任务

要完成任务,就要先取任务 ; 因此在处理请求之前,要先通过accept()从内核中获得请求


我们可以把TCP连接的生成和获得连接的过程看作一个生产者消费者模型。

socket中会包含一个管理连接的队列,这个队列是每个socket都有一份,相互之间不会混淆。


3.当服务器执行到accept时,此时如果客户端还没来,accept就会阻塞,直到有客户端连接成功为止。

accept是把内核中已经建立好的连接,拿到应用程序中,返回值是一个socket对象,这个对象就像一个耳麦,既可以说话,也可以听到对反的声音。

也就是通过socket对象就可以和对方进行网络通信


此时这个回显服务器中,涉及到两种socket

1.ServerSocket

相当于是在店外揽客的服务员,揽到客人之后,交给店内的服务员

2.clientSocket

店内负责招待的服务员

4.

scanner和printwriter没有close,并不会导致文件资源暴露

流对象中持有的资源的两个部分

1)内存(对象销毁,内存回收)

2)   文件描述符  scanner和printwriter持有的是inputstream和outpustream的引用

5.服务器怎么感知到客户端下线的

hasNext()在客户端没有发请求的时候,也会阻塞,一直阻塞到客户端发了请求,或者是客户端退出,它就返回了

2.客户端

基本实现流程:

1.从控制台读取用户的输入

2.把输入的内容构造成请求发送给服务器

3.从服务器读取响应

4.把响应显示到控制台上

代码:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;/*** Tcp版本的服务器** 客户端*/public class TcpEchoClient {private Socket socket=null;//要和服务器通信,就需要先知道,服务器所在的位置public TcpEchoClient(String serverIp,int serverPort) throws IOException {//这个new操作就完成了tcp连接的建立socket = new Socket(serverIp, serverPort);}private void start() {System.out.println("客户端启动");Scanner scannerConsole=new Scanner(System.in);try(InputStream inputStream=socket.getInputStream();OutputStream outputStream=socket.getOutputStream()){while(true){//1.从控制台输入字符串System.out.print("->");String request=scannerConsole.next();//2.把请求发送给服务器PrintWriter printWriter=new PrintWriter(outputStream);printWriter.println(request);/*** 不要忘记flush* 确保数据真的发送出去了*/printWriter.flush();//3.从服务器读取响应Scanner scannerNetwork=new Scanner(inputStream);String response=scannerNetwork.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",9090);client.start();}}

二.问题和解决方法

1.服务器问题

1.关闭当前的socket!!放在finally当中

客户端会有很多,而每个客户端都有一个socket,如果不关闭会消耗大量的资源。

2.(重点!上面的代码是修改后的!)

两个以上(包含)客户端发来的请求,服务器无法正确地处理。

这是因为当第一个客户端来了,accept会返回,进入processConnection

在处理这个客户端请求过程中,即使第二个客户端来了,也无法第二次调用accept

解决办法:改进成多线程

主线程:负责accept,和客户端建立连接

然后创建新的线程,让新的线程去处理客户端的各种请求

更好的办法:使用线程池!

这样可以避免频繁创建和销毁线程。

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

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

相关文章

酷开科技 | 酷开系统沉浸式大屏游戏更解压!

随着家庭娱乐需求日益旺盛,越来越多的家庭消费者和游戏玩家开始追求大屏游戏带来的沉浸感。玩家在玩游戏的时候用大屏能获得更广阔的视野和更出色的视觉包围感,因此用大屏玩游戏已经成为了一种潮流。用酷开系统玩大屏游戏,过瘾又刺激&#xf…

对比Vue2和Vue3的自定义指令

一、自定义指令简介 自定义指令是Vue提供的能力,用于注册自定义的指令,从而实现一些自定义的DOM操作。 二、Vue2中自定义指令 在Vue2中,自定义指令通过全局方法Vue.directive()进行注册: // 注册全局指令v-focus Vue.directive(focus, {inserted: function(el) {el.focus()…

ubuntu 安装 gnome 安装 xrdp

先安装xrdp 更新 apt-get sudo apt-get update && apt-get upgrade安装图形包 apt-get install xubuntu-desktop安装 xrdp apt-get install xrdp安装 xfce4 apt-get install xfce4配置 xfce4 Add xfce to the xfce desktop window manager autorun by fixing the …

rstudio server 服务器卡死了怎么办

欢迎关注weixin:生信小博士 #rstudio 卡死了怎么办 cd ~/.local/share/ ls rm -fr rstudio.old mv ~/.rstudio ~/.rstudio.oldcd ~/.config/ rm -fr .rstudio.old mv ~/.config/rstudio/ ~/.config/rstudio.oldps -ef|grep t040413 |grep rsession |awk {print $2}| xarg…

LabVIEW基于机器视觉的钢轨表面缺陷检测系统

LabVIEW基于机器视觉的钢轨表面缺陷检测系统 机器视觉检测技术和LabVIEW软件程序,可以实现轨道工件的表面质量。CMOS彩色工业相机采集的图像通过图像预处理、图像阈值分割、形态分析、特征定位和图案匹配进行处理和分析。图形显示界面采用LabVIEW软件编程设计&…

postgresql|数据库|序列Sequence的创建和管理

前言: Sequence也是postgresql数据库里的一种对象,其属性如同索引一样,但通常Sequence是配合主键来工作的,这一点不同于MySQL,MySQL的主键自增仅仅是主键的属性做一个更改,而postgresql的主键自增是需要序…

上云容灾如何实现碳中和-万博智云受邀参加1024程序员节数据技术论坛并发表演讲

近日,2023长沙中国1024程序员节在长沙召开。 长沙中国1024程序员节继2020年后已成功连续举办三届,逐步成为 IT 行业引领技术前沿、推动应用创新发展的高影响力年度盛会。是 IT 领域新技术、新产品、新服务的重要发布平台。 万博智云CEO Michael受邀参加…

深度学习_4_实战_直线最优解

梯度 实战 代码: # %matplotlib inline import random import torch import matplotlib.pyplot as plt # from d21 import torch as d21def synthetic_data(w, b, num_examples):"""生成 Y XW b 噪声。"""X torch.normal(0,…

手把手教你玩转单目摄像头(OpenCv+Python)

目录 ​编辑 一,单目应用前景 二,打开摄像头 三,设置分辨率 四,摄像头拍照 五,录制视频 六,单目结合OpenCV的实际应用 一,单目应用前景 单目视觉(monocular vision&#xff0…

Android MQTT连接阿里云使用Json解析数据

Android Studio 连接阿里云订阅主题然后使用JSON解析数据非常好用 导入MQTT的JAR包1、在项目中添加依赖然后使用Studio 去下载库2、直接下载JAR包,然后作为库进行导入 环境验证:给程序进行联网权限XML布局文件效果如下: MainActitive.java 主…

JavaScript进阶 第四天笔记——深浅拷贝、this绑定、防抖节流

JavaScript 进阶 - 第4天 深浅拷贝 浅拷贝 首先浅拷贝和深拷贝只针对引用类型 浅拷贝:拷贝的是地址 常见方法: 拷贝对象:Object.assgin() / 展开运算符 {…obj} 拷贝对象拷贝数组:Array.prototype.concat() 或者 […arr] 如…

k8s-----18、Ingress(对外服务)

Ingress 1、Ingress概念2、 pod和ingress的关系3、 Ingress的工作流程4、 使用步骤5、对外暴露应用实战5.1 创建nginx应用,对外暴露端口使用NodePort5.2 部署ingress controller5.3 创建ingress规则5.4 访问 1、Ingress概念 k8s 对外暴露服务(service&am…

day50 --动态规划9

198.打家劫舍 213.打家劫舍II 337.打家劫舍III 第一题:打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一…

堆(二叉树,带图详解)

一.堆 1.堆的概念 2.堆的存储方式 逻辑结构 物理结构 2.堆的插入问题 3.堆的基本实现(代码)(以小堆为例) 1.堆的初始化 2. 向上调整 3.插入结点 4. 交换函数、堆的打印 5.向下调整 6.删除根节点并调整成小根堆 7.获取堆…

3D模型格式转换工具HOOPS Exchange助力SIMCON搭建注塑项目

行业:设计与制造 / 注塑成型 / 模拟 挑战:注塑成型商面临着以高效的方式为客户生产零件的挑战。需要大量的试验才能生产出适合的零件,同时模具需要进行多次物理修改,每次修改周期最长需要四个星期,成本高达四到五位数…

第1章 Java、IDEA环境部署与配置

JavaEE简介与IDE环境部署 课程目录 JavaEE简介JDK环境部署IntelliJ IDEA环境部署 JavaEE简介 1. JavaEE是什么? Java EE(Java Platform,Enterprise Edition)是sun公司(2009年4月20日甲骨文将其收购)推…

Java反射获取内部类方法

Java反射获取内部类方法 结论一、案例准备二、测试方法:使用反射获取类的成员内部类和方法具体操作具体操作(使用getDeclaredClasses) 结论 Java 通过反射可以获得内部类,包括内部类属性信息和方法。 一、案例准备 创建了一个类…

Python自动处理pptx:新建、另存、添加幻灯片、添加标题、插入文本图片图形、提取文本

Python-pptx库是一个用于创建、更新和读取Microsoft PowerPoint .pptx 文件的Python库。它允许我们使用Python脚本自动化PowerPoint文件的创建、更新和读取操作,是一个非常方便自动化处理PPTX的工具。 安装 pip install python-pptx创建 from pptx import Prese…

【Note】链式存储结构

设计不同的结点结构,可以构成不同的链式存储结构。常用的有:二叉链表、三叉链表、线索链表(用空链域存放指向前驱或后继的线索)。 二叉链表存储 VS 一般二叉树 二叉链表 VS 二叉树 知识点: 一个二叉链表由根指针root…

vue3+vite在线预览pdf

效果图 代码 <template><div class"pdf-preview"><div class"pdf-wrap"><vue-pdf-embed :source"state.source" :style"scale" class"vue-pdf-embed" :page"state.pageNum" /></div…