(原创)当server碰到server,socket连接池简单实现) 选择自 jsand 的 Blog

 

当我们在程序中启动了一个server,这个server需要跟另一个server(比如建立在老系统上的老的c程序)之间通讯,为了增加效率需要建立一个连接池的时候不妨考虑下我的解决方案,不足处清指正。

1:SocketAdapter类,此类继承了socket,重载了socket类的close方法,目的是当用户关闭socket的时候,我们并不关闭它只是放在连接池内部。
package com.tarena.socketpool;
import java.net.*;
import java.io.IOException;
/**
 * <p>socket连接的简单实现</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright Tarena(c) 2005</p>
 * <p>Company: Tarena</p>
 * @author chengxing
 * @version 1.0
 */
public class ConnectionAdapter extends  Socket{
  /**
   * 连接状态
   */
  private boolean status=true;
  /**
   * 默认的构造函数
   */
  public ConnectionAdapter() {
    super();
  }
  public ConnectionAdapter(String host,int port)throws UnknownHostException,IOException{
    super(host,port);
  }
  /**
   * 判断此连接是否空闲
   * @return boolean 空闲返回ture,否则false
   */
  public boolean isFree(){
    return status;
  }
  /**
   * 当使用此连接的时候设置状态为false(忙碌)
   */
  public void setBusy(){
    this.status=false;
  }
  /**
   * 当客户端关闭连接的时候状态设置为true(空闲)
   */
  public void close(){
    System.out.println("Close : set the status is free " );
    status=true;
  }
  public void destroy(){
    //Close socket connection
    close();
   // System.out.println("Close success " );
  }
}
第二个类连接管理器。
package com.tarena.socketpool;

import java.lang.reflect.*;
import java.util.Properties;
/**
 * <p>连接管理器</p>
 * <p>Copyright: Copyright Tarena(c) 2005</p>
 * <p>Company: Tarena</p>
 * @author chengxing
 * @version 1.0
 */
public class ConnectionManager {
  //测试程序默认的连接池实现类
  public static final String PROVIDER_CLASS="com.tarena.socketpool.MyConnectionProvider";
  //测试程序的默认ip
  public static final String HOST="127.0.0.1";
  //测试程序的默认端口号
  public static final String PORT="9880";
  /**
   * 注册钩子程序的静态匿名块
   */
  static {
       //增加钩子控制资源的释放周期
       Runtime runtime = Runtime.getRuntime();
       Class c = runtime.getClass();
       try {
           Method m = c.getMethod("addShutdownHook", new Class[] { Thread.class } );
           m.invoke(runtime, new Object[] { new ShutdownThread() });
       }
       catch (NoSuchMethodException e) {
         e.printStackTrace();
           // Ignore -- the user might not be running JDK 1.3 or later.
       }
       catch (Exception e) {
           e.printStackTrace();
       }
   }
   /**
    * 默认的构造函数
    */
   public ConnectionManager() {
   }
   /**
    * 得到并初始化一个连接池
    * 连接池的实现类通过系统参数来传递进来,通过命令行-DConnectionProvider=YourImplClass
    * 如果没有指定的实现的话,则采用系统默认的实现类
    * 通过命令行传入的参数列表如下
    * 对方主机名-DHost=192.168.0.200
    * 对方端口号 -DPort=9880
    * 最小连接数  -DMax_size=10
    * 最大连结数 -DMin_size=20
    * 以上的值可以改变,但是参数不能改变,
    * 最大连结数和最小连接数可以省略,默认值分别为20和10
    * @return ConnectionProvider
    */
   public static ConnectionProvider getConnectionProvider()throws Exception{
     String provider_class=System.getProperty("ConnectionProvider");
     if(provider_class==null)provider_class=PROVIDER_CLASS;

     String host=System.getProperty("Host");
     if(host==null)host=HOST;

     String port=System.getProperty("port");
     if(port==null)port=PORT;

     String max_size=System.getProperty("Max_size");
     String min_size=System.getProperty("Min_size");

     Properties pro=new Properties();
     pro.setProperty(ConnectionProvider.SERVER_IP,host);
     pro.setProperty(ConnectionProvider.SERVER_PORT,port);
     if(max_size!=null)pro.setProperty(ConnectionProvider.MAX_SIZE,max_size);
     if(min_size!=null)pro.setProperty(ConnectionProvider.MIN_SIZE,min_size);
     //通过反射得到实现类
     System.out.println(provider_class);
     System.out.flush();
     Class provider_impl=Class.forName(provider_class);
     //由于是单子模式,采用静态方法回调
     Method m=provider_impl.getMethod("newInstance",new Class[]{java.util.Properties.class});
     ConnectionProvider provider=null;
     try{
        provider = (ConnectionProvider) m.invoke(provider_impl, new Object[]{pro});
     }catch(Exception e){
       e.printStackTrace();
     }

     return provider;
   }
   /**
    *
    * <p>一个钩子的线程: 在程序结束的时候调用注销连接池</p>
    * <p>Description: </p>
    * <p>Copyright: Copyright Tarena(c) 2005</p>
    * <p>Company: Tarena</p>
    * @author chengxing
    * @version 1.0
    */
   private static class ShutdownThread extends Thread {
          public void run() {
            try{
              ConnectionProvider provider = ConnectionManager.getConnectionProvider();
              if (provider != null) {
                provider.destroy();
              }
            }catch(Exception e){
              e.printStackTrace();
            }
          }
      }


}

第三个类,连接池的接口定义
package com.tarena.socketpool;

import java.net.*;
import java.util.*;
import java.io.IOException;

/**
 *
 * <p>定义的抽象类,所有的子类必须单子模式去实现,
 * 统一方法为public ConnectionProvider newInstance();
 * 连接提供器的抽象接口,每一个实现它的子类最好都是JAVABEAN,
 * 这样它的方法就可以是被外界控制</p>
 * @see JiveBeanInfo
 * <p>Copyright: Copyright Tarena(c) 2005</p>
 * <p>Company: Tarena</p>
 * @author chengxing
 * @version 1.0
 */
public interface
    ConnectionProvider {
  public static final String SERVER_IP = "SERVER_IP_ADDRESS";
  public static final String SERVER_PORT = "SERVER_IP_PORT";
  public static final String MAX_SIZE = "MAX_SIZE";
  public static final String MIN_SIZE = "MIN_SIZE";

  /**
   *判断连接池内是否有连接
   * @return true 有连接返回true,否则返回false
   */
  public boolean isPooled();

  /**
   * 当此方法被调用的时候提供一个 socket
   * @see Socket
   * @return Socket a Connection object.
   */
  public Socket getConnection() throws java.net.SocketException;

  /**
   * 连接池初始化
   */
  public void init() throws UnknownHostException, IOException;

  /**
   * 连接池重新启动
   */
  public void restart() throws UnknownHostException, IOException;

  /**
   * 注销连接池
   */
  public void destroy();
}
第四个类MyConnectionProvider,自己写的一个连接池的简单实现
package com.tarena.socketpool;

import java.util.*;
import java.net.*;
import java.net.SocketException;
import java.io.IOException;

/**
 *
 * <p>这是一个连接管理器的简单实现</p>
 * <p>Description: implements the Interface ConnectionProvider</p>
 * <p>Copyright: Copyright Tarena(c) 2005</p>
 * <p>Company: Tarena</p>
 * @author chengxing
 * @version 1.0
 */
public class MyConnectionProvider
    implements ConnectionProvider {

  private Properties pro = null;
  private static ConnectionProvider provider = null;
  private static Object object_lock = new Object();
  private String ip;
  private String port;

  /**
   * 默认的最大连接数
   */
  private int max_size = 20;

  /**
   * 默认的最小连接数
   */
  private int min_size = 10;

  /**
   * Socket connection池数组
   */
  private ConnectionAdapter[] socketpool = null;

  /**
   * 构造对象的时候初始化连接池
   * @throws UnknownHostException 未知的主机异常
   * @throws IOException
   */
  private MyConnectionProvider(Properties pro) throws UnknownHostException,
      IOException {
    ip = pro.getProperty(SERVER_IP);
    port = pro.getProperty(SERVER_PORT);
    String max_size_s = pro.getProperty(MAX_SIZE);
    String min_size_s = pro.getProperty(MIN_SIZE);
    if (max_size_s != null) {
      max_size = Integer.parseInt(max_size_s);
    }
    if (min_size_s != null) {
      min_size = Integer.parseInt(min_size_s);
    }

    init(); //构造对象的时候初始化连接池
  }

  /**
   * 判断是否已经池化
   * @return boolean 如果池化返回ture,反之返回false
   */
  public boolean isPooled() {
    if (socketpool != null) {
      return true;
    }
    else return false;
  }

  /**
   *返回一个连接
   * @return a Connection object.
   */
  public Socket getConnection() {
    Socket s = null;
    for (int i = 0; i < socketpool.length; i++) {
      if (socketpool[i] != null) {
        //如果有空闲的连接,返回一个空闲连接,如果没有,继续循环
        if (socketpool[i].isFree()) {
          s = socketpool[i];
          return s;
        }
        else continue;
      }
      else { //如果连接为空,证明超过最小连接数,重新生成连接
        try {
          s = socketpool[i] = new ConnectionAdapter(ip, Integer.parseInt(port));
        }
        catch (Exception e) {
          //never throw
        }
      }
    }
    //如果连接仍旧为空的话,则超过了最大连接数
    if (s == null) {
      try { //生成普通连接,由客户端自行关闭,释放资源,不再由连接池管理
        s = new Socket(ip, Integer.parseInt(port));
      }
      catch (Exception e) { //此异常永远不会抛出
      }
    }
    return s;
  }


  /**
   * 初始化连接池
   * @throws UnknownHostException 主机ip找不到
   * @throws IOException 此端口号上无server监听
   */
  public void init() throws UnknownHostException, IOException {

    socketpool = new ConnectionAdapter[max_size];
    for (int i = 0; i < min_size; i++) {
      socketpool[i] = new ConnectionAdapter(ip, Integer.parseInt(port));
      System.out.print(" . ");
    }
    System.out.println();
    System.out.println("System init success ....");
  }

  /**
   * 重新启动连接池
   * @throws UnknownHostException
   * @throws IOException
   */
  public void restart() throws UnknownHostException, IOException {
  destroy();
  init();
  }

  /**
   * 注销此连接池
   */
  public void destroy() {
    for (int i = 0; i < socketpool.length; i++) {
      if (socketpool[i] != null) {
        ConnectionAdapter adapter = (ConnectionAdapter) socketpool[i];
        adapter.destroy();
        System.out.print(" . ");
      }
    }
    System.out.println("/ndestory success ....");
  }
  /**
   * 静态方法,生成此连接池实现的对象
   * @param pro Properties 此连接池所需要的所有参数的封装
   * @throws UnknownHostException 主机无法找到
   * @throws IOException 与服务器无法建立连接
   * @return ConnectionProvider 返回父类ConnectionProvider
   */
  public static ConnectionProvider newInstance(java.util.Properties pro) throws
      UnknownHostException, IOException {
    if (provider == null) {
      synchronized (object_lock) {
        if (provider == null) {
          provider = new MyConnectionProvider(pro);
        }
      }
    }
    return provider;
  }
  /**
   *设置系统属性 通过封装系统properties对象来封装所需要的不同值
   * SERVER_IP,SERVER_PORT,MAX_SIZE,MIN_SIZE等父类定义的不同的参数
   * @param pro Properties 传进来的系统属性
   */
  public void setProperties(Properties pro) {
    this.pro = pro;
  }
}

<think>好的,用户现在遇到了在运行json-server时出现的ERR_SOCKET_BAD_PORT错误,想知道原因和解决方案。我需要先理解这个错误的具体情况。根据错误名称,这通常与端口设置有关,可能是端口号无效或者被占用。 首先,参考用户提供的引用,引用[1]提到修改Apache的httpd.conf文件中的端口配置,比如将默认80端口改为8088以避免冲突。这说明端口被占用是一个常见问题。引用[2]中的Python代码示例用于检查服务器连接,这可能帮助用户测试端口是否可用。引用[3]涉及静态端口定义,而引用[4]讨论了HTTP协议的结构,虽然这些可能不太直接相关,但可以辅助理解端口配置的基础知识。 接下来,用户的问题集中在json-server的ERR_SOCKET_BAD_PORT错误。可能的原因包括:端口号超出有效范围(0-65535),但通常合法端口是1024-65535,且用户可能使用了系统保留端口(如0-1023)。另外,端口号可能被其他应用占用,或者格式错误,比如非数字字符。还可能用户尝试在无权限的情况下使用特权端口(如80或443)。 解决方案方面,首先需要验证端口号是否合法,是否在有效范围内,并且未被占用。用户可以使用类似引用[2]中的代码来检查端口是否可达,或者使用命令行工具如netstat、lsof(Linux/Mac)或资源监视器(Windows)来查看端口占用情况。然后,确保在启动json-server时使用正确的端口参数,如--port 4000,并避免使用保留端口。如果问题依旧,可能需要检查json-server的配置文件是否存在语法错误或冲突的设置。此外,如果用户之前有配置过代理或其他服务,可能需要调整相关设置以避免冲突。 用户可能还遇到权限问题,比如在Linux/Mac上使用低于1024的端口需要sudo权限,所以建议使用高于1024的端口。另外,检查是否有多个json-server实例同时运行,导致端口重复使用。如果所有方法都试过还是不行,可能需要重启系统或重新安装json-server,确保软件本身没有损坏。 最后,结合用户之前的问题,他们在设置json-server的新端口时可能遇到冲突,现在出现错误可能是配置过程中的疏漏。需要指导用户逐步排查,从验证端口有效性到检查占用情况,再到正确配置参数,确保每一步都正确执行。</think>### 深度解析:json-server ERR_SOCKET_BAD_PORT 错误 --- #### ▍错误本质 `ERR_SOCKET_BAD_PORT` 是Node.js底层网络模块抛出的系统级错误,直接反映**端口配置违反TCP/IP协议规范**[^1]。具体到json-server场景,常见原因如下: --- #### ▍核心原因排查 ```mermaid graph TD A[错误触发] --> B{端口合法性检查} B -->|端口范围| C[0 < port ≤ 65535] B -->|保留端口| D[1-1023需管理员权限] B -->|格式错误| E[非数字/空值/非法字符] B -->|特殊端口| F[被系统服务占用] ``` --- #### ▍场景化解决方案 **1. 端口值非法(最常见)** - **现象**:使用`$ json-server --port abcd` - **验证方法**: ```javascript // 直接复制以下代码到Node环境验证(引用[2]扩展) console.log(Number.isInteger(parseInt("abcd"))); // 输出false ``` - **修复方案**: ```bash # 正确格式(引用[1]的端口修改逻辑) json-server --watch db.json --port 4000 # 4000需为整数 ``` **2. 使用保留端口** - **系统保留端口冲突**(如80/443): ```bash # Linux/Mac需sudo权限(危险操作) sudo json-server --port 80 --watch db.json # 建议改用高位端口(引用[3]的端口设计) json-server --port 3001 --watch db.json ``` **3. 端口被占用(隐性冲突)** - **检测工具**: ```powershell # Windows(引用[4]的协议层检测) netstat -ano | findstr ":4000" # Linux/Mac lsof -i :4000 ``` - **动态切换方案**: ```bash # 使用portfinder自动选择可用端口(需安装npm包) npx portfinder --basePort=4000 json-server --port $(cat port.txt) --watch db.json ``` --- #### ▍配置陷阱排查 **package.json脚本错误**(引用[1]的配置延伸): ```diff "scripts": { - "mock": "json-server --watch db.json --port 四零零零" # 中文数字错误 + "mock": "json-server --watch db.json --port 4000" } ``` **环境变量污染**: ```bash # 检查环境变量中的异常PORT设置 echo $PORT # Linux/Mac echo %PORT% # Windows # 临时清除影响 unset PORT # Linux/Mac set PORT= # Windows ``` --- #### ▍高级调试技巧 **1. Node.js底层追踪**: ```bash # 启用Node调试模式查看详细错误栈 NODE_DEBUG=net json-server --port invalid ``` 输出示例: ``` NET: Error: Port "invalid" is not a valid integer at lookupAndConnect (net.js:1096:13) ``` **2. 网络层抓包验证**: ```bash # 使用tcpdump检测端口绑定情况(Linux/Mac) sudo tcpdump -i lo0 'port 4000' ``` --- ### 典型故障案例 **案例1:浮点数端口** ```bash # 错误命令 json-server --port 4000.5 # 错误信息 Error: Port "4000.5" is not a valid integer ``` **修复**:强制转换为整数 `Math.floor(4000.5)` **案例2:多服务配置冲突** ```javascript // vue.config.js 与 json-server 双重占用 module.exports = { devServer: { port: 4000 } // Vue占用4000 } ``` **修复**:参考[1][3]的端口分配策略,确保服务间端口隔离 --- ### 预防性配置建议 1. **端口池管理**(引用[3]的静态配置思想): ```json // ports.config.json { "dev": { "api": 4000, "web": 3000 }, "test": { "api": 5000, "web": 8080 } } ``` 2. **自动化检测脚本**(扩展引用[2]的检查逻辑): ```python # port_check.py import socket from configparser import ConfigParser config = ConfigParser() config.read('ports.config.json') port = config.getint('dev', 'api') with socket.socket() as s: try: s.bind(('', port)) print(f"端口 {port} 可用") except OSError: print(f"端口 {port} 被占用") ``` --- --相关问题-- 1. 如何用Docker隔离json-server的端口环境? 2. Node.js中如何捕获处理ERR_SOCKET_BAD_PORT异常? 3. json-server同时监听多个端口是否可能? 4. HTTP协议中端口号与URI的标准关系是什么?[^4]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值