Pipe Broken问题
最近在生产上总是遇到pipe broken的问题,包括使用java客户端向ES同步数据,java向kafka发送数据等,都出现了类似的问题,为弄清楚Pipe broken问题产生的根因,参考网上其他的教程(详见参考链接)。设计了服务端和客户端的报错的情况,并进行分析。
一、情况模拟
1.1 服务端报错
Server
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.net.ServerSocket; 6 import java.net.Socket; 7 8 public class Server { 9 10 public static void main(String[] args) { 11 try { 12 ServerSocket ss = new ServerSocket(3113); 13 Socket s = ss.accept(); 14 //client->server 15 DataInputStream is = new DataInputStream(s.getInputStream()); 16 byte[] buf =new byte[1024]; 17 int len = is.read(buf); 18 System.out.println("recv:"+new String(buf,0,len)); 19 20 Thread.sleep(10000); 21 22 //server->client 23 s.getOutputStream().write("1111 server".getBytes()); 24 25 26 27 //client->server 28 DataInputStream in = new DataInputStream(s.getInputStream()); 29 byte[] bytes = new byte[1024]; 30 int length = in.read(bytes); 31 32 try { 33 System.out.println("recv2:"+new String(bytes,0,length)); 34 } catch (Exception e) { 35 e.printStackTrace(); 36 37 System.out.println("--------------"); 38 39 DataOutputStream out = new DataOutputStream(s.getOutputStream()); 40 try { 41 out.write("error write ".getBytes()); 42 } catch (IOException e1) { 43 System.out.println("=============================="); 44 e1.printStackTrace(); 45 } 46 } 47 48 }catch (Exception e){ 49 System.out.println(e.getStackTrace()); 50 e.printStackTrace(); 51 } 52 } 53 }
Client
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.net.Socket; 6 7 public class Client { 8 public static void main(String[] args) { 9 Socket s = null; 10 try { 11 s = new Socket("127.0.0.1", 3113); 12 s.setSoTimeout(5000); 13 14 DataOutputStream out = new DataOutputStream( 15 s.getOutputStream()); 16 17 //client->server 18 out.write("1111 from client".getBytes()); 19 20 // server->client 21 DataInputStream in = new DataInputStream(s.getInputStream()); 22 byte[] bytes = new byte[1024]; 23 int len = in.read(bytes); 24 25 System.out.println("client:" + new String(bytes, 0, len)); 26 27 out.write("2222 from client".getBytes()); 28 29 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } finally { 33 if (s != null) { 34 try { 35 s.close(); 36 } catch (IOException e) { 37 e.printStackTrace(); 38 } 39 } 40 } 41 } 42 }
服务端报错情况
使用tcpdump抓包
tcpdump -i lo
倒数第二步,服务端请求发送12字节。最后一步,客户端发送RST报文.然后服务端就报Broken PIPE错误了。
1.2 客户端报错
Server
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.net.ServerSocket; 6 import java.net.Socket; 7 8 public class Server { 9 10 public static void main(String[] args) { 11 try { 12 ServerSocket ss = new ServerSocket(3113); 13 Socket s = ss.accept(); 14 15 s.setSoTimeout(5000); 16 17 DataInputStream is = new DataInputStream(s.getInputStream()); 18 byte[] buf =new byte[1024]; 19 int len = is.read(buf); 20 System.out.println("recv:"+new String(buf,0,len)); 21 22 // Thread.sleep(10000); 23 24 s.getOutputStream().write("1111 server".getBytes()); 25 26 DataInputStream in = new DataInputStream(s.getInputStream()); 27 byte[] bytes = new byte[1024]; 28 int length = in.read(bytes); 29 30 try { 31 System.out.println("recv2:"+new String(bytes,0,length)); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 35 System.out.println("--------------"); 36 37 DataOutputStream out = new DataOutputStream(s.getOutputStream()); 38 try { 39 out.write("error write ".getBytes()); 40 } catch (IOException e1) { 41 System.out.println("=============================="); 42 e1.printStackTrace(); 43 } 44 } 45 46 }catch (Exception e){ 47 System.out.println(e.getStackTrace()); 48 e.printStackTrace(); 49 } 50 } 51 } 52
Client
1 import java.io.DataInputStream; 2 import java.io.DataOutputStream; 3 import java.io.IOException; 4 import java.io.OutputStream; 5 6 import java.net.Socket; 7 8 9 public class Client { 10 public static void main(String[] args) { 11 Socket s = null; 12 13 try { 14 s = new Socket("127.0.0.1", 3113); 15 // s.setSoTimeout(5000); 16 Thread.sleep(10000); 17 18 DataOutputStream out = new DataOutputStream(s.getOutputStream()); 19 20 try { 21 out.write("1111 from client".getBytes()); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 26 System.out.println("11111111"); 27 28 DataInputStream in = new DataInputStream(s.getInputStream()); 29 byte[] bytes = new byte[1024]; 30 int len = in.read(bytes); 31 32 try { 33 System.out.println("client:" + new String(bytes, 0, len)); 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } 37 38 try { 39 out.write("2222 from client".getBytes()); 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } 43 } catch (Exception e) { 44 e.printStackTrace(); 45 } finally { 46 if (s != null) { 47 try { 48 s.close(); 49 } catch (IOException e) { 50 e.printStackTrace(); 51 } 52 } 53 } 54 } 55 }
客户端报错情况
tcpdump情况
倒数第二步,客户端请求发送17字节,服务端返回RST报名,客户端报错。
二、分析
如下划线部分所说:向某个已收到RST的连接执行写操作时,将会返回EPIPE错误。EPIPE!PIPE!
-
知识准备之RST报文
终止一个TCP连接的正常方式是发送FIN。在发送缓冲区中所有排队数据都已发送之后才发送FIN,正常情况下没有任何数据丢失。但我们有时也可能发送一个RST报文段而不是FIN来中途关闭一个连接。这称为异常关闭。 现在知道RST报文的作用了,那就在大致列一下出现RST报文的场景吧:
- connect一个不存在的端口;
- 向一个已经关掉的连接send数据;
- 向一个已经崩溃的对端发送数据(连接之前已经被建立);
- close(sockfd)时,直接丢弃接收缓冲区未读取的数据,并给对方发一个RST。这个是由SO_LINGER选项来控制的;
- a重启,收到b的保活探针,a发rst,通知b。
通俗的讲
服务端和客户端是建立的两个相应的通道
当向一个写关闭的通道,读数据时。这个操作是允许的(管道里可能还有数据)
但是,向一个读关闭的关闭,写数据时,是不允许的。因为就没有人读了(这也就是报错的原因)
三、产生问题的可能原因
一方向通道写数据时,对方(读方)因为各种原因使连接关闭了,导致写方报错。
那么这个各种原因可能包括:读方
资源不够用,意外关闭了连接
读方设置了读取超时时间,写方操作时间太长
四、参考
https://www.cnblogs.com/cthon/p/9139553.html
https://www.cnblogs.com/liangzs/p/9448575.html
https://www.cnblogs.com/yaowen/p/9726357.html
https://blog.csdn.net/qq_23013625/article/details/52816757 setSotimeout方法