socket异常分析

概述

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

问题原因

端口冲突。
这个问题出现的原因是服务器的端口不够用了,服务器总共有(0-65536)个端口。
异常的原因是服务端new ServerSocket(port)新建socket时,这个port已经占用并进行监听了。
此时可以使用netstat -an命令,查看这个端看到状态发现为Listending。出现这样的问题,只需要找一个没有被占用的端口就可以解决这个问题。

解决方案

查询端口对应的进程信息

使用netstat -anp | grep 2181 (这里的端口号,替换成被占用的端口号,比如tomcat 8080等)

[root]# netstat -anp|grep 7777
tcp        0      0 0.0.0.0:17777           0.0.0.0:*               LISTEN      484/nginx: worker p
tcp6       0      0 :::7777                 :::*                    LISTEN      24452/java
tcp6       0      0 192.168.109.145:7777    192.168.109.144:53537   TIME_WAIT   -

最后一排就是进程id(pid)。

查询进程信息

使用ps -ef | grep 7777

[root]# ps -ef|grep 24452
root     19401 17271  0 16:50 pts/3    00:00:00 grep --color 24452
root     24452  2199  7 Sep06 ?        02:01:53 /usr/java/jdk1.8/bin/java -jar xxx-SNAPSHOT.jar

这样可以看到这个进程到底是什么。
pts那个是只想ps -ef|grep 7777生成的,就好像每次执行jps的时候,就有jps这个进程。

杀掉进程

首先要确保这个进程可以被杀掉,千万不能谁便kill 进程。
kill -9 pid

kill -9 24452

编程注意事项

要正确区分长、短连接。
所谓的长连接就是一经建立就永久保持。
短连接使用的场景是使用完立即关闭:准备数据–>建立连接–>发送数据–>关闭连接。

java.net.SocketException: Connection refused: connect

问题原因

连接拒绝。
该异常发生在客户端进行new Socket(ip, port)操作时,原因是指定ip的地址不能找到(也就是ping 不通),或者ip存在但是指定的端口程序没有启动。
总之:TCP连接请求被拒绝是由于目标服务器上无对应的监听套接字(IP && PORT)。

解决方案

  1. 在客户端ping一下服务器看是否能ping通,如果能ping通(如果服务端把ping禁掉了需要使用其他方式),则在服务器上看指定的端口是否有程序启动,监听是否正常。

编程注意事项

这个问题主要是对长连接的维护。
所谓的维护包括两个方面,首先是检测对方的主动断连(即调用socket的close方法),其次是检测对方的宕机、异常退出、网络不同。

  • 检测对方断连
    一方主动断连,另一方如果再进行读操作,则此时的返回值为-1,一旦检测到对方断连,则应该主动关闭己方的连接(调用Socket的close方法)。
  • 检测对方宕机、异常退出、网络不通
    这些场景的检测通常用的方法是“心跳”。
    也就是双方周期性的发送数据给对方,同时也从对方接收“心跳”,如果联系几个周期都没有收到对方心跳,则可以判断对方或者宕机或者异常退出或者网络不通,此时也需要主动关闭己方连接。客户端可延迟一定时间后重新发起连接,也就是重试,可能可以正常建立连接了。

java.net.SocketException: Socket is closed

问题原因

sokcet被关闭。
该异常在客户端和服务器均有可能发生。
异常的原因是自己主动把socket关闭了(调用socket的close方法),然后自己再对网络连接进行读写操作。

解决方案

排查一下是否自己误操作把socket主动关闭了。

java.net.SocketException: (Connection reset或者Connect reset by peer:Socket write error)

问题分析

就是连接异常断开后,读和写操作引发的异常。
主要是对端的socket关闭了,本端没有感知到,继续对连接进行读写导致的。
该异常在客户端和服务器端均有可能发生,引起该异常的原因有两个:

  1. 对端的socket被关闭(主动关闭或者因为异常退出而引起的关闭),本端没有感知到,发送第一个数据包引发该异常(connect reset by peer)。
  2. 对端退出,但退出时并未关闭该连接,本端如果继续从连接中读取数据则抛出异常(connection reset).

解决方案

服务器的并发连接数超过了其承载量,服务器会将其中一些连接关闭。

服务器关闭了某些连接,客户端感知不到继续读或者写都会报异常。
如果知道实际连接服务器的并发客户端数并没有超过服务器的承载量,则有可能是中了病毒或者木马,引起网络流量异常。
解决方法:可以使用netstat -an 命令查看网络连接情况。

客户端异常关闭导致

  1. 客户端关掉了浏览器或者浏览器按了stop,而服务器还在给客户端发送数据。
  2. 接收端主动关闭了,而服务端还在给客户端发送数据。
    这两种情况一般不会影响服务器,但是如果对异常信息没有特别处理,会在服务器的日志文件中重复出现改异常,造成服务器日志文件过大,影响服务器的运行。
    解决方法:可以对一起异常的部分,使用try … catch捕获异常,然后不输出或者只输出一句提示信息,避免输出全部异常信息。

防火墙的问题

如果网络连接通过防火墙,而防火墙一般都会有超时机制,在网络连接长时间不传输数据时,会关闭这个TCP的会话,关闭后客户端或服务器在读写都会导致异常。
解决方法:如果关闭防火墙,解决了问题,需要重新配置防火墙,或者自己编写程序实现TCP的长连接。实现TCP长连接,需要自己定义心跳协议,每隔一段时间,发送一次心跳协议,双方维持连接。

java.net.SocketException:Broken pipe

问题原因

该异常在客户端和服务器均有可能发送。
在上面的java.net.SocketException:Connect reset by peer:Socket write error异常产生后,如果继续写数据则抛出该异常。
也就是对端关闭了socket,本地继续写数据导致该异常。

java.net.SocketTimeoutException

问题分析

这个异常比较常见,socket超时。
一般有两个地方会抛出这个异常。

  1. connect的时间,这个超时时间由connect(SocketAddress endpoint, int timeout)中的timeout来决定,超过这个时间还连接不上服务器,就好报连接SocketTimeoutException。
  2. 读取socket中数据超时,这个超时时间由setSocketTimeout(int timeout)决定,超过这个读取时间,就会报SocketTimeoutException异常。

解决方案

connet,socketTimeout这两个时间按实际业务场景合理的设置即可。

java.net.SocketException:Too many open files

问题原因

操作系统中打开的文件的最大句柄数受限所致,常常发生在很多个并发用户访问服务器的时候。
因为为了执行每个用户的应用,服务器都要打开很多文件(new一个socket就需要一个文件句柄),这就会导致打开文件的句柄的缺乏。

解决方案

  • 尽量把类打成jar包,因为一个jar包只消耗一个文件句柄,如果不打包,一个类就消耗一个文件句柄。
  • java的GC不能关闭网络连接打开的文件句柄,因为如果没有执行socket.close(),则文件句柄将一直存在,不能被关闭。
  • 考虑设置socket的最大打开数来控制这个问题。
  • 操作系统做相关的设置,增加最大文件句柄数量,ulimit -a 可以查看系统目前资源现在,ulimit -n xxx可以修改,但是这个修改只对当前窗口有效。

Processing of multipart/form-data request failed. Unexpected EOF read on the socket异常分析

问题原因

当从输入流读取数据时,返回的读取字节数如果是-1的话,对于文件IO就表示文件读完了,也就是到了"end of file";如果是一个网络IO就表示输入流被关闭了。
再想想HTTP的知识,对于使用长连接的情况下,服务器通过网络输入流读取数据时,是通过请求头Content-Length来判断请求体是否被读取完的,意思就是服务器先会去解析请求头,
通过Content-Length就会知道请求体数据量的大小,然后再从输入流当中读取相应字节数的数据作为这个请求的请求体。
可以分析得出在正常情况下这个nRead是不应该为-1的,就是还有数据可以读出来,也就是还没有达到Content-Length所指定的数据量,然后输入流就关闭了。
就是服务器读数据的过程中读到了异常的文件结束标识(end of file),可能是客户端上传过程主动取消上传,或者网络不稳定导致数据流中断。
错误日志如下:
org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. Unexpected EOF read on the socket,可以看出在copy文件流的过程中抛出IOException,猜想是文件流的获取有异常,在开发环境进行验证在上传文件的过程中取消文件的上传,错误重现。

解决方案

服务端报异常

  1. 该错误属于用户正常取消文件流操作,错误码直接屏蔽掉。
  2. 在tomcat之前添加缓存,例如在nginx中打开缓存,添加配置proxy_request_buffering on。

客户端报异常

上传文件还没到末尾就被客户端中断,是超出客户端的tomcat文件连接时间。
原因,客户端带宽可能太小,导致上传一个文件花费的时间太长,超过了客户端项目的tomcat的连接时间。
客户端tomcat配置修改:

server.connectionTimeout =180000 (客户端上传连接时长)

参考

TCP/IP:连接服务器失败(错误原因:Connection refused)
java.net.SocketException四大异常解决方案
客户端SpringBoot文件上传:java.io.EOFException: Unexpected EOF read on the socket
客户端文件上传错误:java.io.EOFException: Unexpected EOF read on the socket

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java中的Socket和ServerSocket是基于TCP协议实现的网络编程类,用于在网络上建立连接、传输数据和关闭连接。Socket类用于客户端,ServerSocket类用于服务器端。下面对它们的使用进行分析和总结。 1. Socket使用 Socket类提供了一些方法用于建立连接、发送数据和关闭连接,其中常用的方法包括: - Socket(String host, int port):构造一个新的Socket对象,并连接到指定的主机和端口。 - getInputStream()和getOutputStream():分别返回Socket的输入和输出流,用于读取和写入数据。 - close():关闭Socket连接。 在使用Socket类时,需要注意以下几点: - Socket对象的创建和关闭应该在同一个线程中完成,避免出现资源竞争和线程安全的问题。 - Socket连接建立后,需要及时关闭连接,避免出现资源占用和安全隐患。 2. ServerSocket使用 ServerSocket类用于在服务器端监听客户端连接请求,并创建Socket对象进行通信。常用的方法包括: - ServerSocket(int port):创建一个新的ServerSocket对象,并监听指定端口上的连接请求。 - accept():接受客户端的连接请求,并返回一个新的Socket对象,用于与客户端进行通信。 - close():关闭ServerSocket对象。 在使用ServerSocket类时,需要注意以下几点: - ServerSocket对象应该在程序启动时创建,而不是每次处理连接请求时都创建一个新的ServerSocket对象。 - ServerSocket对象只需要监听一次,即可接受多个客户端的连接请求。 - ServerSocket对象的关闭应该在所有客户端连接都关闭后进行。 总的来说,Socket和ServerSocket是Java中基于TCP协议实现的网络编程类,使用起来需要注意线程安全、资源释放、异常处理等问题。熟练掌握它们的使用,可以帮助我们开发出高效、稳定的网络应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

融极

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值