Programming/JPA ⁄Spring

스프링 websocket 사용시 java.io.IOException: Broken pipe 해결하기.

Shane_Park 2021. 6. 11. 11:28
반응형

스프링 websocket 사용시 java.io.IOException: Broken pipe  해결하기. 


java.io.IOException: Broken pipe
	at sun.nio.ch.FileDispatcherImpl.write0(Native Method)
	at sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:47)
	at sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93)
	at sun.nio.ch.IOUtil.write(IOUtil.java:65)
	at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:469)
	at org.apache.tomcat.util.net.SecureNioChannel.flush(SecureNioChannel.java:149)
	at org.apache.tomcat.util.net.SecureNioChannel.close(SecureNioChannel.java:559)
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.close(NioEndpoint.java:1259)
	at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.doClose(WsRemoteEndpointImplServer.java:173)
	at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.close(WsRemoteEndpointImplBase.java:739)
	at org.apache.tomcat.websocket.WsSession.onClose(WsSession.java:539)
	at org.apache.tomcat.websocket.WsFrameBase.processDataControl(WsFrameBase.java:367)
	at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:296)
	at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
	at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85)
	at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183)
	at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162)
	at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:156)
	at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1629)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

 

스프링에서 웹소켓을 사용하려고 하는데, 자꾸 동기 요청시에 서버에서 에러를 내기 시작했습니다.

대부분 비동기로 페이지를 만들어 뒀는데, 아직 비동기 처리가 되지 않은 페이지 사이를 이동할 때 해당 에러가 자꾸 발생했는데요.

해결을 위해 검색을 몇시간 해보다가 결국 선생님을 찾아갔습니다.

 

const server = "wss://"+location.host+getContextPath()+"/echo";
let socket = null; 


$(function(){
	// 페이지 접속시 자동으로 웹소켓 연결을 수립합니다.
	socket = new WebSocket(server);
	
	// 웹소켓에 data가 도착했을 경우 이벤트를 처리합니다. 
	socket.onmessage = function(event){
		
		let pushData = JSON.parse(event.data);
		let dataType = pushData.dataType;
		let data = pushData.data;
		
		if(dataType == 'alarm'){
			// 새로운 알람이 도착했을 경우에는 알림을 보내주고 종 모양을 바꿔준다.
			getAlarm();
			toastr.success(data);
			
		}else if(dataType == 'login'){
			// 새로운 회원이 도착했을 경우에도 알림을 보내준다.
			toastr.info(data);			
		}
		 
	}
	
})	
	

처음 작성했던 코드 입니다. wss echo 서버를 열어두었는데, 페이지 로드시 해당 주소로 WebSocket 연결을 수립 합니다.

만들고 있는 프로젝트에서 대부분 비동기로 진행되기는 하지만, 아직 비동기 처리를 마저 못한 한개의 연결 구간이 있는데, 그 구간을 지날 때마다 자꾸 서버에서

java.io.IOException: Broken pipe 

를 뿜어 댔습니다. 선생님께서 해결을 해주셨는데, 실수로 404 페이지로 넘어갈 때에도 해당 에러가 발생해서 힌트를 얻었습니다.

새로 연결을 수립하는게 아니고, 연결이 종료 될 때 해당 에러가 발생한다는 건데,

 

 

package best.gaia.websocket;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.google.gson.Gson;

import best.gaia.utils.SessionUtil;
import best.gaia.vo.MemberVO;

public class WebSocketEchoHandler extends TextWebSocketHandler {
	
	@Resource(name="userList")
	private List<WebSocketSession> userList;
	
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		userList.add(session);
		Map<String, String> data = new HashMap<>();
		data.put("dataType", "login");
		MemberVO member = SessionUtil.getMemberVoFromWebsocketSession(session);
		data.put("data", member.getMem_nick() + "님이 로그인 했습니다.<br/>현재 총 " + userList.size() + "명 접속중.");
		String jsonData = new Gson().toJson(data);
		
		for(WebSocketSession user :userList) {
			user.sendMessage(new TextMessage(jsonData));
		}
	}
	
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		userList.remove(session);
//		for(WebSocketSession user :userList) {
//			user.sendMessage(new TextMessage("한명 나갔음. 접속중인원 : " + userList.size()));
//		}
	}
	
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		String data = message.getPayload();
		for(WebSocketSession user :userList) {
			user.sendMessage(new TextMessage(data));
		}
	}
	
}

위 코드에서   afterConnectionClosed  부분을 디버깅을 해봤는데, 커넥션이 문제없이 remove 되기는 했습니다. 단지 Tomcat에서 웹 소켓 연결이 종료 되었다는 상태를 제대로 인지 하지 못해서 에러가 생기는 듯 합니다.

 

JavaScript 에서 beforeunload로 페이지를 떠날 때에 대한 이벤트를 만들어 주고, 소켓을 닫았다는 신호를 보내도록 추가 해 주었습니다.

$(window).on('beforeunload', function(){
	socket.close();
});

이렇게 하니 Broken pipe 에러가 사라졌습니다.

 

그덕분에 아래와 같은 push 알람을 구현 할 수 있었습니다.

해당 에러가 발생하는 분은 socket이 닫히는 부분을 신경 써서 확인 해 보시면 되겠습니다.

반응형