c socket server close 시 클라이언트 종료 감지 문의
글쓴이: 전병욱@Facebook / 작성시간: 화, 2019/09/24 - 9:24오전
안녕하세요.
c socket server close 시 클라이언트 종료 감지 문의드립니다.
server가 종료되면 client도 종료가 안 되어 종료되는 방법 문의드립니다.
source 올려드리니 봐주시면 감사하겠습니다.
echoserver.c
#include "libDefine.h" void signalHandlerServer(int nSignal); int g_nServerSocket = -1; /* server socket file descriptor */ int g_nClientSocket = -1; /* client socket file descriptor */ int main(int argc, char *argv[]) { int nPort = -1; char szMsg[SIZEBUFFER + 1] = {0,}; /* 수신용 버퍼 */ int nReadMessage = 0; /* 받은 메시지 길이 저장 변수 */ struct sockaddr_in stClientAddr = {0,}; /* 클라이언트 주소 저장할 구조체 */ unsigned int uClientAddrSize = sizeof(stClientAddr); /* 클라이언트 주소 구조체의 크기 */ int nWriteMessage = -1; /* signal 처리 */ signal(SIGINT, signalHandlerServer); if (argc != 2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } nPort = atoi(argv[1]); printf("서버 연결 중입니다.\n"); g_nServerSocket = serverSocket(nPort); if(g_nServerSocket < 0) { errHandle(errno, "socket() error \n"); exit(1); } while(1) { g_nClientSocket = accept(g_nServerSocket, (struct sockaddr*) &stClientAddr, &uClientAddrSize); if(g_nClientSocket < 0) { errHandle(errno, "accept() 클라이언트로의 접속 요청이 수락되지 않았습니다.\n"); g_nClientSocket = -1; sleep(1); } else { printf("클라이언트 접속 IP : %s\n", inet_ntoa(stClientAddr.sin_addr)); printf("accept() 클라이언트로 접속 요청 수락\n"); } while((nReadMessage = read(g_nClientSocket, szMsg, SIZEBUFFER)) > 0) { printf("클라이언트로 받은 메시지 %s\n", szMsg); nWriteMessage = write(g_nClientSocket, szMsg, nReadMessage); if(nWriteMessage < 0) { errHandle(errno, "write error \n"); break; } memset(szMsg, 0, sizeof(szMsg)); } close(g_nClientSocket); g_nClientSocket = -1; printf("연결이 끊어졌습니다. \n"); sleep(1); } close(g_nServerSocket); g_nServerSocket = -1; return 0; } void signalHandlerServer(int nSignal) { char szMessage[SIZEBUFFER] = {0,}; if (nSignal == SIGINT) { fputs("종료하시겠습니까?(y 입력) : ", stdout); fgets(szMessage, SIZEBUFFER, stdin); if(!strcasecmp(szMessage, "y\n")) { if(g_nClientSocket >= 0) { close(g_nClientSocket); g_nClientSocket = -1; printf("client socket 종료되었습니다.\n"); exit(0); } if(g_nServerSocket >= 0) { close(g_nServerSocket); g_nServerSocket = -1; printf("server socket 종료되었습니다.\n"); exit(0); } } } }
echoclient
#include "libDefine.h" void signalHandlerClient(int nSignal); int g_nClientSocket = -1; int main(int argc, char *argv[]) { int nPort = -1; char *pszAddr = NULL; /* ip 문자 입력받을 포인터 변수 */ char szMsg[SIZEBUFFER + 1] = {0,}; /* 서버에 보낼 메시지를 저장할 문자열 버퍼 */ int nReadMessage = 0; /* 송수신 메시지의 문자열 길이 */ int nWriteMessage = 0; signal(SIGINT, signalHandlerClient); /* port */ if (argc != 3) { printf("usage : %s <IP> <port> \n", argv[0]); exit(1); } pszAddr = argv[1]; nPort = atoi(argv[2]); g_nClientSocket = clientSocket(nPort, pszAddr); if(g_nClientSocket < 0) { errHandle(errno, "socket() error \n"); exit(1); } while (1) { memset(szMsg, 0, sizeof(szMsg)); fputs("입력 메시지(Q는 종료, 버퍼 사이즈까지 입력할 수 있습니다.) : ", stdout); fgets(szMsg, SIZEBUFFER, stdin); if(!strcasecmp(szMsg, "q\n")) { close(g_nClientSocket); g_nClientSocket = -1; break; } if((nWriteMessage = write(g_nClientSocket, szMsg, strlen(szMsg))) < 0) { errHandle(errno, "write error \n"); close(g_nClientSocket); g_nClientSocket = -1; exit(1); } if((nReadMessage = read(g_nClientSocket, szMsg, SIZEBUFFER)) < 0) { errHandle(errno, "read error \n"); close(g_nClientSocket); g_nClientSocket = -1; exit(1); } szMsg[nReadMessage] = 0; printf("서버로 받은 메시지 : %s\n", szMsg); } return 0; } void signalHandlerClient(int nSignal) { char szMessage[SIZEBUFFER] = {0,}; if (nSignal == SIGINT) { fputs("종료하시겠습니까?(y 입력) : ", stdout); fgets(szMessage, SIZEBUFFER, stdin); if(!strcasecmp(szMessage, "y\n")) { if(g_nClientSocket >= 0) { close(g_nClientSocket); g_nClientSocket = -1; printf("연결 종료되었습니다.\n"); exit(0); } } } }
libDefine.c
#include "libDefine.h" int serverSocket(int nPort) { int nServerSocket = -1; struct sockaddr_in stServerAddr = {0,}; /* 서버 주소 저장할 구조체 */ if(nPort < 0) { errHandle(errno, "port error"); exit(1); } if(nPort) /* TCP 통신용 서버 소켓 생성 */ nServerSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (nServerSocket < 0) { errHandle(errno, "socket() error"); exit(1); } else { printf("socket() TCP 통신용 서버 소켓이 생성되었습니다.\n"); } memset(&stServerAddr, 0, sizeof(stServerAddr)); stServerAddr.sin_family = AF_INET; stServerAddr.sin_addr.s_addr = htonl(INADDR_ANY); stServerAddr.sin_port = htons(nPort); /* bind */ if (bind(nServerSocket, (struct sockaddr*) &stServerAddr, sizeof(stServerAddr)) < 0) { errHandle(errno, "bind() error %d\n", nPort); close(nServerSocket); nServerSocket = -1; exit(1); } else { printf("bind() - socket에 주소와 IP 할당되었습니다.\n"); } /* 소켓을 서버용으로 사용할 수가 있게 한다. */ if (listen(nServerSocket, LISTENQ) < 0) { errHandle(errno, "listen() error %d\n", nPort); close(nServerSocket); nServerSocket = -1; exit(1); } else { printf("listen() 연결 요청 대기하는 중입니다.\n"); } return nServerSocket; } int clientSocket(int nPort, char *pszAddr) { int nClientSocket = -1; struct sockaddr_in stServerAddr = {0,}; /* 접속할 서버의 주소 저장할 구조체 */ if(nPort < 0) { errHandle(errno, "port error"); exit(1); } if(pszAddr == NULL) { errHandle(errno, "address error"); exit(1); } nClientSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if(nClientSocket < 0) { errHandle(errno, "socket() error"); exit(1); } /* 서버 쪽 주소 구조체 0 초기화 */ memset(&stServerAddr, 0, sizeof(stServerAddr)); stServerAddr.sin_family = AF_INET; /* 인터넷 주소체계 사용 */ stServerAddr.sin_addr.s_addr = inet_addr(pszAddr); /* 서버 IP 구조체에 저장 */ stServerAddr.sin_port = htons(nPort); /* 서버 Port 구조체에 저장 */ /* connect */ if (connect(nClientSocket, (struct sockaddr*) &stServerAddr, sizeof(stServerAddr)) < 0) { errHandle(errno, "connect() error %s\n", pszAddr); close(nClientSocket); nClientSocket = -1; exit(1); } else { printf("연결되었습니다.\n"); } return nClientSocket; } /* error 처리 handler */ void errHandle(int nErrorCode, char *pszMsg, ...) { va_list argptr; char szBuf[MESSAGE] = {0,}; if(nErrorCode) { printf("error = %s\n", strerror(errno)); } if(pszMsg == NULL) { printf("message error = %s\n", strerror(errno)); exit(1); } va_start(argptr, pszMsg); vsnprintf(szBuf, MESSAGE, pszMsg, argptr); /* 버퍼에 문자열 쓰고 버퍼 길이 지정하며 va_list 얻는다. */ va_end(argptr); puts(szBuf); }
Forums:
여러 문제가 보이지만,
여러 문제가 보이지만,
클라이언트도 read를 다했으면 loop에서 빠저나와 close() 해줘야죠.
추가로 클라이언트 read에서 버퍼사이즈 많큼 못
추가로 클라이언트 read에서 버퍼사이즈 많큼 못 받아서 무한정 대기중에 빠진건지 확인 해 보세요.
감사합니다.
감사합니다.
클라이언트 시그널 ctrl c 누르면 y 입력하면 close되게 했습니다.
근데 서버에서 ctrl + c 시그널 프로세스가 되면 클라이언트에서 종료되지 않습니다.
클라이언트 read에서는 버퍼만큼 충분히 받고 있는 데 어떻게 수정하면 될까요?
이 참...어디서부터 설명을 해야할지....
이 참...어디서부터 설명을 해야할지....
코드 수정하는 부분은 능력이 안되 가르쳐 드릴 순 없지만,
서버에서 ctrl + c 했다고 클라이언트에 SIGINT 전달 됩니까??
그리고 read는 non-blockg설정을 하지 않는 이상, 버퍼 사이즈 많큼 read되어야 다음 작업을 수행합니다.
감사합니다.
서버에서는 ctrl c 하면 사실 시그널 서버, client를 각각 만들었습니다.
ctrl + c를 하면 서버에 g_nClientSocket이 accept 접속 요청을 수락해서 받는 소켓인 데 이 부분이 서버에서 ctrl + c를 하면 close 되게 했습니다.
지금 하는 부분은 다중 접근이 아닌 단일 통신입니다.
음 ..
client 가 서버의 종료를 감지할 수 있는 순간은, socket 에 write 또는 read 를 하다 실패하는 경우입니다.
그런데, 현재 client 는 fgets() 에서 block 해 있기 때문에..
아무거라도 입력해서 다음 루틴으로 넘어가지 않는 한, socket 에러를 감지할 수 없습니다.
위와 같은 로직에서는 자동으로 종료하게 하는 것은 불가하고..
stdin 과 socket fd 를 select() 로 묶어서 감시하면, 어케 될 수 있을 것 같네요.
되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』
감사합니다.
그럼 저기서 어떻게 추가하면 될까요?
그리고 특정 port는 접속하지 못하게 어떻게 하면 되는지 궁금합니다.
음 ..
select 를 보고 느낌이 없으시다면, 어디서부터 설명해야 할지 막막해 집니다.
적어도 man select 해보셨다면, stdin 에서 입력받는 예제가 있었을 테고..
구글에 select stdin socket 정도만 넣어봤어도, 참고할 만한 코드들이 나오니까요.
대충 아래 글 보시고, 어떻게 쓰는건지 한 번 로직을 살펴 보시고..
아래 글의 '문제를 수정해서' client 코드에 반영해 보세요.
https://stackoverflow.com/questions/39402886/select-on-stdin-and-incoming-socket
되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』
감사합니다.
감사합니다.
그리고 서버 포트에서 1024~49151 port가 주로 많이 사용하는 데 65535 이상은 서버 포트로 사용하지 않게 하려면 예외 처리로 서버에서 bind() 전에 하면 되지 않을까요?
감사합니다.
아마 select나 epoll을 사용해도 무관하지 않나요?
감사합니다.
댓글 달기