上节回顾

套接字,TCP版本的套接字 API

ServerSocket 服务器这边使用的
Socket: 服务器和客户端都需要使用

对于服务器来说:

1.创建ServerSocket 关联上一个端口号   (称为listenSocket)

2.调用ServerSocket 的 accept 方法

accept的功能是把一个内核建立好的连接给拿到代码中处理

accpept会返回一个Socket实例,称为clientSocket

3.使用clientSocket的getlnputStream和getOutputStream

得到字节流对象,就可以进行读取写入了

4.当客户端断开连接之后,服务器就应该要及时的关闭clientSocket.(否则可能会出现文件资源泄漏情况)

对于客户端:

1.创建一个Socket对象.创建的同时指定服务器的ip和端口(这个操作就会让客户端和服务器建立TCP连接.这个连接建立的过程就是传说中的"三次握手",这个流程是内核完成的,用户代码感知不到)

2.客户端就可以通过Socket对象的getlnputStream和getOutputStream来和服务器进行通信了

在上一篇的TCP代码中,其实还存在一个很严重的bug!!!

实际开发中,一个服务器应该要对应很多个客户端!!而且升值是成千上万个.

现在我们在IDEA上再启动一个客户端

现在这里就有两个客户端了

 当前就发现,当启动第二个客户端的时候,服务器就没有提示"上线"

当在第二个客户端发送数据的时候,发现没有任何反应

当退出第一个客户端的时候,神奇的事情出现了!!!服务器提示了客户端2上线,也得到了hello2这样响应!!

总结:当前咱们的服务器同一时刻,只能给一个客户端提供服务,只有前一个客户端下了,下一个客户端才能上来.这样的设定,显然是不科学的.

原因是啥?

解决方案:

使用多线程!

主线程里面循环调用 accept  每次获取到一个连接,就创建一个线程,让这个线程来处理这个连接

服务器代码改进

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;/*** Created with IntelliJ IDEA.* Description:* User: 灯泡和大白* Date: 2022-07-26* Time: 18:36*/
//这个代码和前面普通的 TCP 回显服务器,基本差不多,只不过,这里面增加了多线程的处理//针对每个客户端都搞一个新的线程
public class TcpThreadEchoServer {private ServerSocket listenSocket = null;public TcpThreadEchoServer(int port) throws IOException {listenSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动");while (true) {// 在这个代码中, 通过创建线程, 就能保证 accept 调用完毕之后, 就能立刻再次调用 accept .Socket clientSocket = listenSocket.accept();// 创建一个线程来给这个客户端提供服务Thread t = new Thread() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}};t.start();}}// 这个代码和前面是一样的了.public void processConnection(Socket clientSocket) throws IOException {String log = String.format("[%s:%d] 客户端上线!", clientSocket.getInetAddress().toString(),clientSocket.getPort());System.out.println(log);try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {// 1. 读取请求并解析Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {log = String.format("[%s:%d] 客户端下线!", clientSocket.getInetAddress().toString(),clientSocket.getPort());System.out.println(log);break;}String request = scanner.next();// 2. 根据请求计算响应String response = process(request);// 3. 把响应写回到客户端PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();log = String.format("[%s:%d] req: %s; resp: %s", clientSocket.getInetAddress().toString(),clientSocket.getPort(), request, response);System.out.println(log);}} catch (IOException e) {e.printStackTrace();} finally {clientSocket.close();}}// 回显服务器, 直接把请求返回即可public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpThreadEchoServer server = new TcpThreadEchoServer(9090);server.start();}
}

实际开发中,客户端的数量可能会很多啊.

这个时候能行吗?

虽然线程比进程更轻量,但是如果有很多客户端连接又退出,这就会导致咱们当前的服务器里频繁的创建销毁线程.....

这个时候还是会有很大的成本的

如何改进这个问题?

线程池

线程池版本

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;public class TcpThreadPoolEchoServer {private ServerSocket listenSocket = null;public TcpThreadPoolEchoServer(int port) throws IOException {listenSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");ExecutorService executorService = Executors.newCachedThreadPool();while (true) {Socket clientSocket = listenSocket.accept();// 使用线程池来处理当前的 processConnectionexecutorService.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}public void processConnection(Socket clientSocket) throws IOException {String log = String.format("[%s:%d] 客户端上线!", clientSocket.getInetAddress().toString(),clientSocket.getPort());System.out.println(log);try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {while (true) {// 1. 读取请求并解析Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {log = String.format("[%s:%d] 客户端下线!", clientSocket.getInetAddress().toString(),clientSocket.getPort());System.out.println(log);break;}String request = scanner.next();// 2. 根据请求计算响应String response = process(request);// 3. 把响应写回到客户端PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();log = String.format("[%s:%d] req: %s; resp: %s", clientSocket.getInetAddress().toString(),clientSocket.getPort(), request, response);System.out.println(log);}} catch (IOException e) {e.printStackTrace();} finally {clientSocket.close();}}// 回显服务器, 直接把请求返回即可public String process(String request) {return request;}}

假设极端情况下,一个服务器面临很多很多客户端,这些客户端,连上之后,并没有退出.

这个时候服务器这边同一时刻,就会存在很多很多线程(上万个线程)

这个情况,会有其他的问题嘛?这是科学的处理方法吗?

实际上,如果出现这样的情况,是不科学的!!!

每个线程都要占据一定的系统资源,如果线程太多太多,此时很多系统资源就会非常紧张,达到一定程度,及其可能就扛不住了(宕机了)

系统资源指的是:内存资源,CPU资源

针对这种线程特别多的情况,如何改进呢?

1.可以使用协程来代替线程,完成并发.(很多协程的实现是 M:N) 协程比线程还轻量(GO语言)

2.可以使用IO多路复用的机制,完成并发.

IO多路复用:从根本上解决服务器处理高并发的问题.

在内核里来支持这样的一个功能

刚才假设有1万个客户端,在服务器这边会用一定的数据结构来把这1万个客户端对应的socket都存好.不需要一个线程对应一个客户端了,就一共只有一个/几个线程

IO多路复用机制,就能够做到,那个socket上面有数据了,就通知到应用程序,让这个线程来从这个socket中读数据.

3.使用多个主机(分布式)

提供更多的硬件资源

这三点都是一个基本的来处理一个高并发场景,所使用的的办法

网络通信的原理

网络协议是分层的.

应用层:

应用层协议,是程序猿打交道的最多的协议.

应用层是直接和程序相关的

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

2.程序猿自己定制一个协议,完成需求

协议不是一成不变的.协议也不是遥不可及的.协议很多时候都是程序猿自己约定的.

只要客户端 + 服务器都是自己开发的,这个时候中间使用啥样的协议,完全是咱们自己说了算!!!

现成的应用层协议:

其实也是有很多的.

用的最多的应用层协议HTTP协议.

比如,我在浏览器上,输入一个地址,然后浏览器打开了一个网页.(如果电脑没网,能打的开嘛)

这个过程就是,客户端(浏览器)给bing的服务器发送了一个请求,请求中就包含另一个链接.

然后bing的服务器给浏览器返回了一个响应,这个响应就是一个网页.

在这个网络通信中,使用的应用层协议,就是HTTP协议.(注意,当前很多网站,其实是使用了HTTPS,HTTPS也是基于HTTP).

再比如,手机端,打开了一个饿了么/美团外卖,先看到一个商家列表,点进去,就能看到吃的列表.还有下单,支付......这些过程客户端和服务器之间的通信,大概率也是在使用HTTP协议.

除了HTTP之外,还有很多其他的应用层协议

FTP.现在已经很少见了.以前的时候,文件传输,就可以使用

SSH:后面linux会涉及到.(使用一个终端软件(例如xahell),就可以连接到一个服务器)(在一个遥远的机房里面)

TELNET:现在不常见,如果进行一些嵌入式开发.....

DNS:域名解析的协议

........

应用层协议种类非常多.....

自定义协议,程序猿自己来约定.

约定,请求是啥格式.

响应是啥格式.

客户端和服务器之间就可以按照这样的约定来进行开发了.

(这个事在日常工作中经常会涉及)

假设开发一个外卖软件.

客户端(APP):这一波程序猿开发客户端

服务器:这一波程序猿开发服务器

假设现在有一个需求:要求在外卖软件的首页,就能显示一个"优惠活动",用户参与活动,就能抽取红包.

客户端:就得修改界面,能够显示优惠活动的详情.

服务器:修改逻辑后,针对啥样的用户能参加优惠,具体咋样能领到红包,红包金额是多少...

这个时候客户端启动的时候,就要向服务器查询,当前是否能够参与活动.

服务器就要返回"是"/"否"

这里就要求,客户端和服务器必须统一!!!

【Java成王之路】EE初阶第十篇:(网络编程) 4相关推荐

  1. 【Java成王之路】EE初阶第二十三篇: HTTP协议和Tomcat

    目录 前言 HTTP 协议 什么是HTTP协议 回顾一下应用层协议 理解 HTTP 协议的工作过程 抓包工具的使用 HTTP协议的详细格式 + 信息 HTTP请求的格式 HTTP响应的格式 HTTP协 ...

  2. 【Java成王之路】EE初阶第二十四篇: Servlet

    Sevlet Servlet 是什么 Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一 个 web app. Tomcat 是一个 ...

  3. 【Java成王之路】EE初阶第十四篇:(网络原理) 4

    上节回顾 TCP的核心就是可靠性!! 1.确认应答.保证可靠性的核心机制.针对传输数据以字节为单位进行编号. 2.超时重传: ①.传输的数据丢了 ②.ack应答报文丢了 都要重传 超时时间是动态变化的 ...

  4. Java成神之路技术整理

    转载自 Java成神之路技术整理 以下是Java技术栈微信公众号发布的所有关于 Java 的技术干货,会从以下几个方面汇总,本文会长期更新. Java 基础篇 Java 集合篇 Java 多线程篇 J ...

  5. 详细讲解 —— 网络编程套接字(Java EE初阶)

    网络编程套接字 1 认识网络编程套接字 2 UDP 数据报套接字编程 2.1 UPD服务端 2.1 UDP客户端 2.3 结果测试 3 TCP流套接字编程 3.1 TCP服务端 3.2 TCP客户端 ...

  6. Java EE初阶---网络原理之TCP_IP

    1.网络基础 1.1 认识IP地址 概念         IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址. 作用         IP地址是IP协 ...

  7. Java成神之路[转]

    阿里大牛珍藏架构资料,点击链接免费获取 针对本文,博主最近在写<成神之路系列文章> ,分章分节介绍所有知识点.欢迎关注. 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 ...

  8. 【学海无涯】Java成神之路

    基础篇 面向对象 面向对象与面向过程   面向过程就是按照程序进行的顺序依次编写索要完成相应任务的方法,依次调用.面型对象注重对逻辑概念的封装,将若干变量和方法封装成类,各个对象互相调用.面向对象占用 ...

  9. Alibaba技术专家倾心五年打造 Java成神之路:基础篇

    近日里,很多人邀请我回答各种j2ee开发的初级问题,我无一都强调java初学者要先扎实自己的基础知识,那什么才是Java的基础知识?又怎么样才算掌握了java的基础知识呢?这个问题还真值得仔细思考. ...

最新文章

  1. matlab欧拉迭代,matlab机械臂正逆运动学求解问题,使用牛顿-欧拉迭代算法
  2. 王石:我每天都强迫自己做的一件事
  3. docker yum php mysql_Centos下 使用Docker, 配置PHP+Nginx+Mysql(多PHP版本)
  4. Java后端,应该日常翻看的中文技术网站
  5. php跨域同步登录,织梦PC端移动端会员同步登录跨域AJAX
  6. faster rcnn流程
  7. Dojo QuickStart 快速入门教程 (2) 基本框架
  8. 水题(water)(非详细解答)
  9. Linux使用rostopic echo将rosbag文件转换成csv或txt文件以及sh脚本批量化操作
  10. Camel In Action 读书笔记 (8)
  11. Edmonds-Karp算法
  12. PHP程序员求职的一点思考
  13. 极致体验,揭晓抖音背后的音视频技术
  14. 我写了款依赖检查的插件
  15. 关于当前安全设置不允许下载文件问题的解决
  16. 让Mac文本编辑器成为HTML编辑器
  17. 一个emoji表情包处理工具类
  18. stm32cubeide烧写程序_初学STM32CubeIDE
  19. XPO:Session管理与缓存--测试篇
  20. Warning: masked_scatter_ received a mask with dtype torch.uint8, this behavior is now deprecated,ple

热门文章

  1. 7-135 数字金字塔 (10分)
  2. 算法:百分比,好评率,进度,百分比进度
  3. 2022年03月-电子学会青少年等级考试C语言(一级)真题与解析
  4. 移植 RT-Thread 到MB9BF218S
  5. Java毕业设计:基于jsp+mysql+Spring+SpringMVC+mybatis的网络硬硬盘系統网站
  6. “非工作总结”之快门—我的镜头见过你
  7. 120帧手机动态壁纸_OPPO Find X2支持独立芯片视频动态插帧,30帧以下可升至120帧...
  8. 牛客:使徒袭来,小白兔分身
  9. NOKIA N70如何恢复出厂设置
  10. 基于JAVA呼和浩特市盈锐机电设备有限公司财务管理系统计算机毕业设计源码+数据库+lw文档+系统+部署