网络编程-TCP流套接字

news/2024/12/13 4:46:28/


专栏简介: JavaEE从入门到进阶

题目来源: leetcode,牛客,剑指offer.

创作目标: 记录学习JavaEE学习历程

希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长.

学历代表过去,能力代表现在,学习能力代表未来! 


目录

1.Java 流套接字编程模型

2.SeverSocket API

3.Socket API

4.TCP 中的长短连接

5. 代码示例:


1.Java 流套接字编程模型


2.SeverSocket API

SeverSocket 是创建 TCP 服务端 Socket 的 API.

SeverSocket 的构造方法:

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字 Socket,并绑定到指定端口

ServerSocket 方法:

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

3.Socket API

Socket 是客户端 Socket , 或服务器接收到客户端建立连接(accept方法) 的请求后 , 返回的服务端 Socket.

不管是客户端还是服务端 Socket , 都是双方建立连接后 , 保存的对端信息 , 及用来与对方收发数据的.

Socket 构造方法:

方法签名方法说明
Socket(String host,int port)创建一个客户端流套接字Socket,并与对应的 IP 主机上,对应的端口号建立连接.

 Socket 方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回套接字的输入流
InputStream getOutputStream()返回此套接字的输入流

4.TCP 中的长短连接

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

  • 短连接: 每次接收数据并返回响应后 , 都关闭连接(短连接只能收发一次数据)
  • 长连接: 不关闭连接 , 一直保持连接状态 , 双方不停的收发数据(长连接可以多次的收发数据)

对比以上长短连接 , 两者区别如下:

  • 建立连接 , 关闭连接的耗时: 短连接每次请求 , 响应都需要建立连接 , 关闭连接; 而长连接只需要第一次建立连接 , 之后的请求 , 响应都可以直接传输. 相对来说连接的建立和关闭都是需要耗时的 , 因此长连接更加高效.
  • 主动发送请求不同: 短连接一般是客户端主动向服务器发送请求; 而长连接客户端主动发送请求 , 也可以是服务端主动发送.
  • 两者的使用场景不同: 短连接适用于客户端请求频率不高的场景 , 如浏览网页等. 长连接适用于客户端与服务端通信频繁的场景 , 如聊天室 , 实时游戏等.

扩展了解:

基于BIO(同步阻塞IO) 的长连接会一直占用系统资源. 对于并发要求很高的服务系统来说 , 这样的消耗是不能承受的.

由于每个连接都需要不停的阻塞等待接收数据 , 所以每个连接都会在一个线程中运行.

一次阻塞对应着一次请求 , 响应 , 不停处理也就是长连接的特性: 一直不关闭连接 , 不停的处理请求.

实际应用时: 服务端一般是基于 NIO (即同步非阻塞IO) 来实现长连接 , 性能可以极大的提升.(IO多路复用)


5. 代码示例:

服务器:

public class TcpEchoSever {private ServerSocket serverSocket = null;public TcpEchoSever(int port) throws IOException {this.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 scan = new Scanner(inputStream);if (!scan.hasNext()) {//没有下个数据 , 说明读完了(客户端关闭了连接)System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());break;}//此处使用的 next 返回结果中不包含空白符.String request = scan.next();//2.根据请求计算响应String response = process(request);//3.返回响应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 {try {clientSocket.close();} catch (IOException e) {throw new RuntimeException(e);}}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoSever server = new TcpEchoSever(9090);server.start();}
}

客户端:

public class TcpEchoClient {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.println(">");String request = scanner.next();if(request.equals("exit")){System.out.println("goodbye");break;}//2. 把读到的内容构造成请求发送给服务器PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(request);printWriter.flush();//3. 读取服务器的响应Scanner respScanner = new Scanner(inputStream);String response = respScanner.next();//4. 把响应显示到界面上System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1" , 9090);tcpEchoClient.start();}
}

由这段代码可知 , 服务器每连接一个客户端 , 就会阻塞等待客户端发送请求 , 如果客户端不主动断开连接 , 该线程就会处于占用状态 , 因此单线程情况下 , 一个服务器只能连接一个客户端.

 public void start() throws IOException {System.out.println("启动服务器!");while (true) {//使用这个 clientSocket 和具体的客户端进行交流.Socket clientSocket = serverSocket.accept();processConnection(clientSocket);}}

 为了满足连接多个客户端的需求 , 我们需要创建多个线程  , 但不断地创建/销毁线程 , 需要很大的开销 , 因此可以考虑使用线程池.

public void start() throws IOException {System.out.println("启动服务器!");ExecutorService threadPool = Executors.newCachedThreadPool();while(true){//使用这个 clientSocket 和具体的客户端进行交流.Socket clientSocket = serverSocket.accept();//多线程版本
//            Thread t = new Thread(()->{
//                processConnection(clientSocket);
//            });
//            t.start();//线程池版本threadPool.submit(()->{processConnection(clientSocket);});}}

运行结果:

 

 

如果开启多个客户端?

 

 


http://www.ppmy.cn/news/22705.html

相关文章

【算法基础】冒泡排序解析

作者:柒号华仔 个人主页:欢迎访问我的主页 个人信条:星光不问赶路人,岁月不负有心人。 个人方向:专注于5G领域,同时兼顾其他网络协议,编解码协议,C/C,linux等,感兴趣的小…

【C语言】详细介绍qsort和模拟实现qsort

🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝 📣系列专栏:凡人修C传 💬总结:希望你看完之后&…

【TypeScript】TS 看这一篇就够了

文章目录🧑‍💻TypeScript基本概念TypeScript 是什么?为什么要有typescript安装编译 TS 的工具包编译并运行 TS 代码创建基于TS的vue项目🧑‍💻TypeScript基础类型注解TypeScript类型概述TypeScript原始数据类型数组类…

SSM整合配置

SSM整合配置1. module结构2. pom.xml3. resources3.1 jdbc.properties:4. config4.1 SpringConfig:4.2 JdbcConfig:4.3 MybatisConfig:4.4 SpringMvcConfig:4.5 ServletInitializer:★1. module结构 SSM整…

【数据结构基础】图 - 最小生成树(Prim Kruskal)

Kruskal算法是从最小权重边着手,将森林里的树逐渐合并;prim算法是从顶点出发,在根结点的基础上建起一棵树。最小生成树相关名词连通图: 在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。强连通…

【Web开发】Python实现Web服务器(CentOS下运行Flask)

文章目录1、简介2、安装2.1 安装Centos2.2 安装python2.3 安装虚拟环境2.4 修改国内源2.5 安装flask库3、测试3.1 flask官方例子结语1、简介 CentOS 大家应该很熟悉了,英文全称:Community Enterprise Operating System(社区企业操作系统&…

Spring Cloud Gateway源码

文章目录一、背景1. 核心概念2. 工作流程3.动态路由二、自动配置源码解析1.GatewayAutoConfiguration2.WebFluxAutoConfiguration3.HttpHandlerAutoConfiguration三、请求过程源码解析1. 整体流程2. NettyWebServer接收http请求3.请求和响应转换4.HttpWebHandlerAdapter5. Exce…

怎么使用【Davinci Cfg】配置CanNM Bus load reduction功能

文章目录 1 涉及的模块2 配置的参数传送门 ==>> AutoSAR入门和实战系列总目录 1 涉及的模块 配置CanNM Bus load reduction功能要用到NM和CanNM模块,NM是抽象出来的,它独立于使用的具体总线,CanNM是CAN总线的NM. 2 配置的参数 要配置使用某个通道的Bus load reduc…