3分钟手写TCP连接池,并基于Socket通信模拟康宝和桐哥的微信在线聊天

news/2024/11/24 12:10:04/

文章目录

    • TCP连接池
    • 客户端
    • 服务端

TCP连接池

设计

  1. ConcurrentHashMap<Integer, Socket>作为连接池
  2. 声明连接池的数量、端口和ip、TCP连接的状态(boolean数组)
  3. 初始化连接池
  4. 使用Socket只需要找到状态是未连接的即可
  5. 使用之后将此时连接状态设为未连接
package com.ossa.issavior.socket;import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;/*** TCP连接池的实现*/
public class SocketBuild {/*** 连接池数量*/private static final int CONNECTION_POOL_SIZE = 10;/*** ip*/private static final String API_SERVER_HOST = "127.0.0.1";/*** 端口*/private static final int API_SERVER_PORT = 8989;private static SocketBuild self = null;/*** 连接池*/private ConcurrentHashMap<Integer, Socket> socketPool = null;/*** 连接的状态(true-被占用,false-空闲)*/private boolean[] socketStatusArray = null;/*** 初始化连接池,最大TCP连接的数量为10*/private static synchronized void init() {self = new SocketBuild();self.socketPool = new ConcurrentHashMap<>();// 初始化连接数量self.socketStatusArray = new boolean[CONNECTION_POOL_SIZE];//初始化连接池System.out.println("初始化连接池.");buildConnectionPool();}/*** 建立连接池*/private synchronized static void buildConnectionPool() {if (self == null)init();System.out.println("准备建立连接池.");Socket socket;try {for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {socket = new Socket(API_SERVER_HOST, API_SERVER_PORT);self.socketPool.put(i, socket);self.socketStatusArray[i] = false;}} catch (Exception e) {System.out.println("连接池建立失败!");throw new RuntimeException(e);  }System.out.println("连接池建立成功.");}/*** 从连接池中获取一个空闲的Socket** @return 获取的TCP连接*/public static Socket buildConnection() {if (self == null)init();int i;for (i = 0; i < CONNECTION_POOL_SIZE; i++) {if (!self.socketStatusArray[i]) {self.socketStatusArray[i] = true;break;}}if (i <= CONNECTION_POOL_SIZE) {System.out.println("连接池中的第" + i + "个连接");return self.socketPool.get(i);} else {System.out.println("从连接池建立连接失败,没有空闲的连接");throw new RuntimeException("No enough pooled connection");}}/*** 当获得的socket不可用时,重新获得一个空闲的socket。** @param socket 不可用的socket*/public static void rebuildConnection(Socket socket) {if (self == null)init();Socket newSocket = null;try {for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {if (self.socketPool.get(i) == socket) {System.out.println("重建连接池中的第" + i + "个连接");newSocket = new Socket(API_SERVER_HOST, API_SERVER_PORT);self.socketPool.put(i, newSocket);self.socketStatusArray[i] = true;}}} catch (Exception e) {System.out.println("重建连接失败!");throw new RuntimeException(e);}}/*** 将用完的socket放回池中,调整为空闲状态。此时连接并没有断开。** @param socket 使用完的socket*/public static void releaseConnection(Socket socket) {if (self == null) {init();}for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {if (self.socketPool.get(i) == socket) {self.socketStatusArray[i] = false;System.out.println("释放连接 " + i);break;}}}/*** 断开池中所有连接*/public synchronized static void releaseAllConnection() {if (self == null)init();//关闭所有连接Socket socket;for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {socket = self.socketPool.get(i);try {socket.close();self.socketStatusArray[i] = false;} catch (Exception e) {e.printStackTrace();}}System.out.println("全部连接已经关闭。");}/*** 重新建立连接池。*/public static void reset() {self = null;System.out.println("重建连接池...");init();}
}

客户端

设计

  1. 主线程负责从控制台获取内容(死循环),发送给服务端
  2. 子线程负责获取服务端发送的数据
package com.ossa.issavior.socket;import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class 康宝 {private final ExecutorService executorService = Executors.newCachedThreadPool();public static void main(String[] args) {康宝 client=new 康宝();client.startAction();}public void startAction(){Socket socket=null;BufferedReader reader=null;BufferedWriter writer=null;BufferedReader reader2=null;try {socket = SocketBuild.buildConnection();reader = new BufferedReader(new InputStreamReader(System.in));reader2=new BufferedReader(new InputStreamReader(socket.getInputStream()));writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));readSocketInfo(reader2);String lineString="";while(!(lineString=reader.readLine()).equals("exit")){writer.write(lineString+"\n");writer.flush();}} catch (Exception e) {e.printStackTrace();} finally {try {if (reader!=null) {reader.close();}if (writer!=null) {writer.close();}if (socket!=null) {SocketBuild.rebuildConnection(socket);}} catch (Exception e2) {e2.printStackTrace();}}}void readSocketInfo(BufferedReader reader){executorService.submit(()->{try {String lineString="";while( (lineString = reader.readLine())!=null ){System.out.println(lineString);}} catch (Exception e) {e.printStackTrace();}});}
}

服务端

  1. 通过死循环开启长连接,开启线程去处理消息
package com.ossa.issavior.socket;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class 桐哥 {private final ExecutorService executorService = Executors.newCachedThreadPool();public static void main(String[] args) {桐哥 socketServer = new 桐哥();socketServer.startAction();}private final static Logger LOGGER = LoggerFactory.getLogger(桐哥.class);public void startAction() {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(8989);  //端口号//通过死循环开启长连接,开启线程去处理消息while (true) {Socket socket = serverSocket.accept();executorService.submit(() -> {BufferedReader reader = null;BufferedReader readerIn = null;BufferedWriter writer = null;try {reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//读取客户端消息readerIn = new BufferedReader(new InputStreamReader(System.in));//读取客户端消息writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//向客户端写消息String lineString = "";while (!(lineString = reader.readLine()).equals("bye")) {LOGGER.info("康宝发来消息:" + lineString);writer.write("桐哥发来消息:" + readerIn.readLine() + "\n");writer.flush();}} catch (Exception e) {e.printStackTrace();} finally {try {if (reader != null) {reader.close();}if (writer != null) {writer.close();}if (socket != null) {socket.close();}} catch (Exception e2) {e2.printStackTrace();}}});}} catch (Exception e) {e.printStackTrace();} finally {try {if (serverSocket != null) {serverSocket.close();}} catch (Exception e2) {e2.printStackTrace();}}}}

简易版演示:

在这里插入图片描述
在这里插入图片描述


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

相关文章

前端里最帅的2016年终总结

年底了&#xff0c;我奋战到最后一刻&#xff0c;虽说我很帅&#xff0c;但是 免不了俗。 人人都写各种年终总结&#xff0c;年终告白&#xff0c;虽说大部分人是在应付公司&#xff08;我不揭穿你们额&#xff09;&#xff0c;但如果是自己真心实意的想写&#xff0c;确实是有…

【HMS Core】运动健康服务发起授权失败

【问题描述】 集成运动健康服务&#xff0c;按照官方文档Demo发起授权&#xff0c;提示授权失败&#xff0c;错误码{"mData":{"mExtras":{"mMap":{"HEALTHKIT_AUTH_RESULT":"{\"status\":{\"statusCode\":1…

什么是康宝光驱

COMBO在英文里的意思是“结合物”&#xff0c;而康宝&#xff08;COMBO&#xff09;驱动器就是把CD—RW刻录机和DVD光驱结合在一起的“复合型一体化”驱动器。简单的说&#xff0c; COMBO就是集CD—ROM、DVD—ROM、CD-RW三位一体的一种光存储设备。 康宝光驱的倡导者&#xff…

pc机

PC (personal computer)&#xff0c;个人 计算机一词源自于1981年 IBM的第一部桌上型 计算机型号 PC&#xff0c;在此之前有 Apple II的个人用计算机。能独立运行、完成特定功能的个人计算机。个人计算机不需要 共享其他计算机的处理、 磁盘和打印机等资源也可以独立工作。今天…

为什么刻盘显示计算机内存不够,dvd刻录机在刻录时显示内存不足需终结cd怎么办...

肯定是你没刻录进去 1、不要超容量刻,特别是DVD+-R和CD-R,因为一旦刻出问题,DVD+-R和CD-R盘的特性将导致刻在其上的数据无法擦除,该盘就只能作废处理,所以即使是与刻录机同品牌的盘也最好不要超容量刻。如要关闭NERO中的超容量刻功能,运行NERO BURNING ROM,点"文件…

打架打出来的牛逼城市!

公众号后台回复“图书“&#xff0c;了解更多号主新书内容作者&#xff1a;挖数来源: 挖数根据2020年城市分级名单&#xff0c;佛山跟成都、杭州、南京等一起被列为新一线城市&#xff0c;在我印象中&#xff0c;这样一座城应该处处是高楼&#xff0c;商超遍地是&#xff0c;节…

拆机记(1)

家里有几台旧的台式电脑主机&#xff0c;打算趁在家的时间&#xff0c;把它们拼拼凑凑弄一台好一点的放在家里用&#xff0c;顺便也了解一下计算机各部件的组成&#xff0c;想来对于学习计算机组成与结构是会有不小的帮助的。正好也借着博客&#xff0c;把这个过程给记录下来。…

【布隆过滤器】世界上大概有1 亿种小蛋糕,客户康宝要求这辈子不吃重复的小蛋糕。

文章目录 需求概念思想问题优点缺点应用手写布隆过滤器补充 需求 现在客户康宝有一个需求&#xff1a;世界上大概有 1 亿 种小蛋糕&#xff0c;康宝要求这辈子不吃重复种类的小蛋糕。 因为小蛋糕的种类很大可能只会增加&#xff0c;而不会减少&#xff0c;面对这种大数据量的…