Linux支持的最大TCP连接数 & Socket IO 常见异常

目录

Client最大TCP连接数

TCP的端口是否可被共用?

重启 TCP 服务进程时,为什么会有“Address in use”的报错信息?

java.net.BindException: Address already in use: JVM_Bind

java.net.ConnectException: Connection refused: connect | java.net.ConnectException: Connection timed out: connect

java.net.SocketException: Socket is closed

 java.net.SocketException: Socket input is already shutdown  |  Socket output is already shutdown

java.net.SocketException: Software caused connection abort: socket write error

java.net.SocketException: Cannot send after socket shutdown: socket write error

 java.net.SocketTimeoutException: Read timed out

java.net.SocketException:  Connection reset  | Connection reset by peer: socket write error

Netty设置连接队列长度


Client最大TCP连接数

TCP端口的数据类型是unsigned short,因此本地端口个数最大只有65536,端口0有特殊含义,不能使用,这样可用端口最多只有65535

client每次发起TCP连接请求时,除非绑定端口,通常会让系统随机选一个空闲的本地端口,该端口是独占的,不和其他TCP连接共享。

而对server端,通过增加内存、修改最大文件描述符个数等参数,单机最大并发TCP连接数超过10万没啥问题(C10K问题)。

ulimit -a (自定义设置)

TCP的端口是否可被共用?

协议 + (源IP地址,源端口,目的IP地址,目的端口)才确定唯一通道。

  • TCP 和 UDP 在内核中是两个完全独立的模块,当服务端收到数据包后会依据不同协议转发。所以它两可以使用同一端口!
  • TCP 连接是由四元组(源IP地址,源端口,目的IP地址,目的端口)唯一确认的,那么只要四元组中任意一个元素发生了变化就表示不同的 TCP 连接。
    所以如果客户端已使用端口 8080 与服务端 A 建立了连接,那么客户端要与服务端 B 建立连接,还是可以使用端口 8080 的。一般而言,客户端在执行#connect 函数时从内核里随机独占式选择一个端口,然后向服务端发起 SYN 报文;客户端不建议使用 bind 函数,应该交由 客户端connect 函数来自主选择。
  • 开启 net.ipv4.tcp_tw_reuse  内核参数(客户端(连接发起方) 在调用 connect() 函数时才起作用),就会判断TCP四元组的连接状态如果处于 TIME_WAIT 状态,且该状态持续时间超过了 1 秒,那么就会重用该连接。 如果没开启,则内核遍历寻找可用端口至到端口资源耗尽,connect()函数报错返回错误。

重启 TCP 服务进程时,为什么会有“Address in use”的报错信息?

当我们重启 TCP 服务进程的时候,服务器端发起了关闭连接操作会经过四次挥手;而对于主动关闭方,会在 TIME_WAIT 这个状态里停留一段时间,这个时间大约为 2MSL。

TIME_WAIT 状态的连接使用的 IP+PORT 仍然被认为是被占用的,相同机器上不能够在该 IP+PORT 组合上进行绑定,所以报错。

可以在调用 bind 前,对 socket 设置 SO_REUSEADDR 属性来解决这个问题。它的作用有

  1. 如果当前启动进程绑定的 IP+PORT 有存在处于TIME_WAIT 状态的连接占用,但是新启动的进程使用了 SO_REUSEADDR 选项,那么该进程就可以绑定成功。
  2. 绑定的 IP地址 + 端口时,只要 IP 地址不是正好(exactly)相同,那么允许绑定。(这也解决了ip是 ‘0.0.0.0’ 无法与其他ip绑定同一端口的问题 )

java.net.BindException: Address already in use: JVM_Bind

该异常发生在服务器端启动  new ServerSocket(port)(port是一个0,65536的整型值)操作时:

同一个端口只能被一个ServerSocket绑定监听,否则报错:

   Exception in thread "main" java.net.BindException: Address already in use: JVM_Bind
	at java.net.DualStackPlainSocketImpl.bind0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketBind(Unknown Source)
	at java.net.AbstractPlainSocketImpl.bind(Unknown Source)
	at java.net.PlainSocketImpl.bind(Unknown Source)
	at java.net.ServerSocket.bind(Unknown Source)
	at java.net.ServerSocket.<init>(Unknown Source)
	at java.net.ServerSocket.<init>(Unknown Source)
	at com.noob.learn.bio.BioServer.main(BioServer.java:37)

准确的说:

如果两个 TCP 服务进程同时绑定的 IP 地址和端口都相同,那么执行 bind() 时候就会出错,错误是“Address already in use” 。 需要注意的是,如果ip是 ‘0.0.0.0’ 也会报错,因为它代表任意地址。 

java.net.ConnectException: Connection refused: connect | java.net.ConnectException: Connection timed out: connect

该异常发生在客户端进行 new Socket(ip, port)操作时: ip | port有错  或 服务端没启动... 。

java.net.ConnectException: Connection refused: connect
	at java.net.DualStackPlainSocketImpl.connect0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
	at java.net.Socket.connect(Socket.java:589)
	at java.net.Socket.connect(Socket.java:538)
	at java.net.Socket.<init>(Socket.java:434)
	at java.net.Socket.<init>(Socket.java:211)
	at com.noob.bio.BioClient.main(BioClient.java:32)
java.net.ConnectException: Connection timed out: connect
	at java.net.DualStackPlainSocketImpl.connect0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
	at java.net.Socket.connect(Socket.java:589)
	at java.net.Socket.connect(Socket.java:538)
	at java.net.Socket.<init>(Socket.java:434)
	at java.net.Socket.<init>(Socket.java:211)
	at com.noob.bio.BioClient.main(BioClient.java:32)

java.net.SocketException: Socket is closed

该异常在客户端和服务器均可能发生。异常的原因是: 一方主动关闭了连接后(调用了Socket#close方法)再对连接进行读写操作。

java.net.SocketException: socket closed
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
	at java.io.FilterInputStream.read(FilterInputStream.java:107)
	at com.noob.bio.RequestHandler.handleWithoutLineBreak(RequestHandler.java:85)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:50)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)

 java.net.SocketException: Socket input is already shutdown  |  Socket output is already shutdown

在关闭输入流Socket#shutdownInput() 后, 再次关闭:

java.net.SocketException: Socket input is already shutdown
	at java.net.Socket.shutdownInput(Socket.java:1525)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:58)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)

 在关闭输入流Socket#shutdownOutput() 后, 再次关闭 :

java.net.SocketException: Socket output is already shutdown
	at java.net.Socket.shutdownOutput(Socket.java:1555)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:59)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)

java.net.SocketException: Software caused connection abort: socket write error

软件原因导致连接中断 。

关闭数据接收Socket#shutdownInput() ,此时#read不再能读到客户端上送报文, 但同时也导致写出异常

java.net.SocketException: Socket input is already shutdown
	at java.net.Socket.shutdownInput(Socket.java:1525)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:58)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)
java.net.SocketException: Software caused connection abort: socket write error
	at java.net.SocketOutputStream.socketWrite0(Native Method)
	at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:143)
	at com.noob.bio.RequestHandler.keepOut(RequestHandler.java:70)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:53)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)

本例以8082主动发起shutdownInput ,直接将Tcp关闭了

主动发出了RST :

java.net.SocketException: Cannot send after socket shutdown: socket write error

关闭数据写出 socket#shutdownOutput:

java.net.SocketException: Socket output is already shutdown
	at java.net.Socket.shutdownOutput(Socket.java:1555)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:59)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)
java.net.SocketException: Cannot send after socket shutdown: socket write error
	at java.net.SocketOutputStream.socketWrite0(Native Method)
	at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:143)
	at com.noob.bio.RequestHandler.outwrite(RequestHandler.java:136)
	at com.noob.bio.RequestHandler.handleWithoutLineBreak(RequestHandler.java:86)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:50)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)

此时: 依然可以接受到客户端的上送数据

 服务端主动shutdownOutput之后:进入到FIN_WAIT_2 状态,(主动发起了FIN, 并收到了客户端的ACK);  客户端进入到了 CLOSE_WAIT 状态。

 主动发出了FIN :

 java.net.SocketTimeoutException: Read timed out

Socket#setSoTimeout 设置 客户端和服务连接建立后据传输过程中数据包之间间隔的最大时间 ;

InputStream#read() 会阻塞超时,该选项必须在进入阻塞操作之前启用才能生效!应用在ServerSocket.accept()、SocketInputStream.read() 、DatagramSocket.receive()这几种阻塞场景。

只要服务端不要在捕获该Exception后就直接关闭sokcet,依然可以拿socketInputStream处理 ,Socket仍然有效。

eg. 设置1秒超时,如果每隔0.8秒传输一次数据,传输10次,总共8秒,这样是不超时的。而如果任意两个数据包之间的时间超过了1秒,则超时SocketTimeoutException 。

java.net.SocketTimeoutException: Read timed out
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
	at java.io.FilterInputStream.read(FilterInputStream.java:107)
	at com.noob.bio.RequestHandler.handleWithoutLineBreak(RequestHandler.java:85)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:50)
	at java.lang.Thread.run(Thread.java:748)
10:26:25.579 [Thread-0] INFO com.noob.bio.RequestHandler - 接收到客户端/127.0.0.1:61985的信息: 客户端/127.0.0.1:61985的慰问2
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
10:26:25.579 [Thread-0] INFO com.noob.bio.RequestHandler - 发出信息->>>>服务端的感谢1, 因为: 客户端/127.0.0.1:61985的慰问2
                                                                                                                            

java.net.SocketException:  Connection reset  | Connection reset by peer: socket write error

  • 一端返回了RST指令时(eg. Socket被主动关闭或异常退出),此时另一端还在往Socket发送数据,发送的第一个数据包引发该异常: “Connection reset by peer”。
java.net.SocketException: Connection reset by peer: socket write error
	at java.net.SocketOutputStream.socketWrite0(Native Method)
	at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:143)
	at com.noob.bio.RequestHandler.outwrite(RequestHandler.java:138)
	at com.noob.bio.RequestHandler.handleWithoutLineBreak(RequestHandler.java:86)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:50)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)
  • 一端返回了RST时,此时另一端正在从Socket里读数据则会提示 “Connection reset”;
// 客户端异常退出时,服务端read()抛出异常
java.net.SocketException: Connection reset
	at java.net.SocketInputStream.read(SocketInputStream.java:154)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
	at java.io.FilterInputStream.read(FilterInputStream.java:107)
	at com.noob.bio.RequestHandler.handleWithoutLineBreak(RequestHandler.java:85)
	at com.noob.bio.RequestHandler.run(RequestHandler.java:50)
	at com.noob.bio.BioServer.requestHandler(BioServer.java:73)
	at com.noob.bio.BioServer.main(BioServer.java:65)

那什么时候服务端会发送RST指令报文呢?

  1. 当收到TCP报文,但是发现该报文不是已建立的TCP连接列表可处理的,则其直接向对端发送reset报文RST;
  2. 服务器的TCP全连接队列(accept queue)(队列里连接状态为ESTABLISHED,长度是: min(/proc/sys/net/ipv4/tcp_max_syn_backlog[半连接队列大小],/proc/sys/net/core/somaxconn[全连接队列长度]))溢出。
    1. tcp_abort_on_overflow  = 1 时 :  向Client回复RST,表示连接无法建立;
    2. tcp_abort_on_overflow  = 0 时 : 直接丢弃Client发过来的ack , 也不回应; 这样就会导致在Client中的连接一直处在ESTABLISHED状态却又无法正常工作。
  3. 防火墙的问题...
  •  开启了syncookies的话( TCP参数 net.ipv4.tcp_syncookies  = 1  ),将连接信息编码在ISN(initialsequencenumber)中返回给客户端,这时服务端不需要将半连接保存在队列中,而是利用客户端随后发来的ACK带回的ISN还原连接信息,以完成连接的建立;避免了半连接队列被攻击SYN包填满,半连接队列(sync queue)(队列里连接状态为SYN_RECV)就相当于是无限大的; 
  •  没开的话: 半连接队列的长度将为 /proc/sys/net/ipv4/tcp_max_syn_backlog 。此时对半连接填满时的处理策略是:server将丢弃请求连接的SYN, 不回复SYN+ACK, 这样就会造成Client端收不到握手响应,始终处在SYN_SENT状态。经过几次重传后,客户端 #connect() 调用失败。

Netty设置连接队列长度

默认设置见方法: NetUtil#SOMAXCONN

也可以通过Option方法自定义backlog的大小 :

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
        .channel(NioServerSocketChannel.class)        // NIO
        .option(ChannelOption.SO_BACKLOG,128) // 临时存放已完成三次握手的请求的队列的最大长度。 如果大于队列的最大长度,请求会被拒绝
        .childOption(ChannelOption.SO_KEEPALIVE,true) // 长连接
        .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception { }
        });

原生Socket则是在创建ServerSocket时指定,最终由方法 ServerSocket#bind -> SocketImpl#listen 绑定后开启监听时使用。

 

在创建Socket|ServerSocket后设置
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 为了最大化服务端的websocket连接,可以考虑以下几点: 1. 使用高效的websocket服务器,如Node.js的WebSocket库、Socket.IO等,可以提高连接的稳定性和性能。 2. 优化服务端的网络环境,包括网络带宽、延迟等,以确保连接的稳定性和速度。 3. 采用负载均衡技术,将连接分散到多个服务器上,以提高服务的可靠性和性能。 4. 优化客户端的网络环境,包括网络带宽、延迟等,以确保连接的稳定性和速度。 5. 使用心跳机制,定时检测连接的状态,及时处理异常情况,以保证连接的稳定性。 6. 优化代码逻辑,减少不必要的计算和网络请求,以提高服务的性能和响应速度。 以上是最大化服务端的websocket连接的一些方法和建议,希望能对您有所帮助。 ### 回答2: 要最大化服务端的websocket连接,可以考虑以下几个方面: 1. 提高服务器的性能:通过优化服务器硬件设备、增加服务器带宽和处理能力等方式,提高服务器的性能水平,能够更好地支持更多的并发连接。 2. 使用高效的框架和库:选择使用经过优化和性能良好的websocket框架和库,如Netty、Node.js等,能够提高服务器处理websocket连接的效率和稳定性。 3. 单线程模型:采用单线程模型能够提供更好的性能表现。因为websocket连接是长连接,一个连接对应一个线程会导致并发连接数过多时线程开销过大,而选择单线程模型可以减少线程切换的开销和资源消耗。 4. 负载均衡和集群部署:通过使用负载均衡器来将请求分发到多个服务端节点上,可以提高websocket连接的并发处理能力。同时,构建一个websocket的集群部署,将连接分布在多个服务器上,能够提高可用性和性能。 5. 异步处理:合理利用异步IO和非阻塞IO的机制,在处理连接请求时可以提高处理效率,多路复用技术可以提高连接的并发处理能力。 6. 合理配置参数:根据实际情况,合理配置服务器连接数、缓冲区大小等参数,提高服务器对websocket连接的支持能力。 7. 定期清理长时间闲置的连接:及时清理长时间闲置的连接,及时释放资源,避免资源浪费和性能下降。 8. 使用压测工具进行性能测试:通过使用压测工具对服务器进行性能测试,找出性能瓶颈和优化空间,进一步优化服务端的websocket连接。 ### 回答3: 要最大化服务端的WebSocket连接,可以采取以下几个方法: 1. 提高服务器的性能:确保服务器硬件配置足够强大,以支持大量的并发连接。可以使用高性能的服务器硬件,如多核CPU和大容量内存,来提高服务器的处理能力。 2. 调整服务器的网络设置:针对WebSocket连接,可以优化服务器的网络设置。例如,通过调整TCP/IP参数来提高连接的可靠性和稳定性,如增加TCP连接数的限制,增加连接的超时时间等。 3. 使用负载均衡:通过使用负载均衡来分发WebSocket连接,可以将连接分散到多台服务器上,从而提高系统的容量和可伸缩性。可以使用硬件负载均衡器或软件负载均衡器来实现连接的分发和管理。 4. 优化代码和算法:在WebSocket服务器端的代码中,可以通过优化代码和算法来提高连接的处理效率和性能。例如,可以使用多线程或异步IO等技术来处理并发连接,减少锁竞争和资源消耗。 5. 进行性能测试和优化:通过进行性能测试,可以发现系统的瓶颈和性能问题,并进行相应的优化。可以使用性能测试工具模拟大量的WebSocket连接,以测试服务器的承载能力,并根据测试结果进行性能优化。 总之,要最大化服务端的WebSocket连接,需要综合考虑服务器硬件、网络设置、负载均衡、代码优化和性能测试等因素,并根据实际情况进行相应的优化和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值