Coding Memo

Listen Backlog에 대한 Connection 주의사항 본문

메모

Listen Backlog에 대한 Connection 주의사항

minttea25 2024. 2. 2. 16:13

Summary

1. 소켓이 listening 상태에서 Accept를 하지 않고 backlog도 여유가 있을 경우에 클라이언트에서 이 소켓에 Connect를 하게 된다면 실제로 연결이 되지 않았지만(Accept 되지 않았지만) 클라이언트에서는 Connect에 성공한 것으로 간주될 수 있다.

따라서 이 때, connect에 관련한 IO callback이 호출될 수 있고, SocketError 값 또한, Success로 받아들인다.

위 상황에서 서버는 실제로 Accept를 한 상태가 아니기 때문에 서버에서는 Connected로 간주되지 않는다.

(다시 말하자면, accept를 하지 않고 있기 때문에 backlog(대기열)에만 연결 시도한 소켓이 추가되었을뿐 서버는 이 상태를 연결상태로 받아들이질 않는다.)

 

2. 위 문제에 대한 이유는, 클라이언트 소켓이 서버의 backlog(대기열)에 추가가 되었다는 것 자체로 클라이언트에서는 연결이 되었다고 간주될 수 있기 때문이다.

 

3. 위 상태에서 데이터를 전송할 때, 전송 자체에는 에러가 되지는 않는다. 실제로 서버로 데이터를 전송한다. 서버에서는 실제로 연결된 상태가 아니기 때문에, 그 데이터는 대기열에서 그 소켓에 대해 마찬가지로 pending 상태가 된다. 클라이언트에서 데이터를 전송했지만, 서버는 아직 이를 받지 않는 상태가 된 것이다. 이후에, 서버에서 해당 소켓을 accpet를 하고 실제로 연결이 된다면, 그 소켓에 pending 되어 있던 데이터가 수신된다. 여기서 주의해야 할 점은 클라이언트에서 timeout을 지정하였을 경우에, (가짜로) 연결된 소켓을 닫고 이후에 다시 그 소켓에 대해서 서버에 연결을 한다면 서버와 마찬가지로 연결 성공과 동시에 pending 되어 있던 데이터가 클라이언트로 한번에 수신 될 것이다.

 


connection 수를 서버에서 조절하려고 하다가 발견한 문제(?) 이다...

 

내가 겪었던 상황은 이렇다. (로컬 환경이다.)

 

서버는 연결 클라이언트를 1개로 한정 짓기 위해 accept 후 semaphore를 이용하여 accept를 기다리도록 한 상태이다.

(이 때 backlog는 100이다.)

 

1. 클라이언트 A가 서버에 접속하고 연결상태가 된다.

2. 위 1번 상태에서 클라이언트 B가 서버에 접속을 시도한다.

3. 서버는 연결 수를 제한하고 있기 때문에 서버에서는 클라이언트 B에 대한 연결이 성립되지 않는다. (AccpetAsync에 대한 callback이 실행 되지 않는다.)

4. 그러나 클라이언트 B에서는 ConnectAsync에 대한 callback이 실행되고 SocketError 또한 Success이다.

5. 클라이언트에서 연결을 확인하고 데이터를 전송해도 서버에서는 해당 데이터를 받지 못한다. (즉, 클라이언트 요청에 대한 응답을 처리하지 않는다.)

 

만약 클라이언트 B에서 서버와는 연결이 되었지만 이후 데이터 요청에 대한 응답을 받지 못하여 (= timeout) 서버 측에서 disconnect를 호출 하면 어떻게 될까?

=> 클라이언트에서는 disconnect가 정상적으로 호출이 된다.

=> 서버에서는 당연히 이를 인지하지 못한다.

 

여기서 부터는 어떤 이유로 인해 나타나는 문제인 것 같다.

 

보통은 disconnect가 되면 backlog에서도 해당 요청이 삭제될 것이다.

 

문제는 이 이후였다. 클라이언트 A가 서버와의 연결을 정리했을 때, backlog에 있던 클라이언트 B의 연결 요청의 잔재(?)가 accept되었다. 이후 클라이언트 B의 요청또한 accept 이후에 받아진다. 서버에서는 이 요청에 대한 로직 수행 후, 응답을 보냈다. 내 경우에는 backlog에서 요청이 삭제되지 않았던 것이었다. 클라이언트에서도 마찬가지로 서버에 연결을 했을 때, 이전 요청에 대한 응답이 연결 직후에 바로바로 수신되는 문제가 있었다. 

예를 들어, 이전 요청이 로그인 요청이었을 경우, 서버에 연결됨과 동시에 이전 로그인 요청에 대한 응답이 수신되었다. 물론 이 응답은 보내지 않은 요청 (정확히는 이전 클라이언트 앱에서 보낸 요청)에 대한 응답이다.

 

일단 위 현상에 대한 문제는 클라이언트의 요청이 backlog에서 제거되지 않아서 나타나는 문제이고, 이 이유는 로컬 환경에서의 동작방식에 의해 일어나기 때문으로 보인다.


 

결론적으로는 위와 같은 현상에 대해서도 생각을하고 서버와 클라이언트에서 적절히 처리를 해주어야 할 필요가 있다.