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을 사용해도 무관하지 않나요?
감사합니다.
댓글 달기