소켓 통신의 문제점.. 과연 무엇일까요..ㅠㅠ
안녕하세요
리눅스로 서버를 구현중입니다.
클라이언트는 윈도우기반 닷넷입니다.
접속하는 모든 클라이언트에게 브로드캐스팅 하는 구조이구요
클라이언트에게 3초마다 한번씩 데이터를 전송합니다.
문제는 서버가 종료될때인데요
서버가 정상종료가 아닌 강제종료
ex) kill -9 서버의프로세스ID
이렇게 죽여버리면 클라이언트가
null값인 데이터를 계속 받네요
과연 무엇이 문제 일까요
서버의 전송버퍼에 남아있는 데이터를 서버 프로세스가 죽은 후에도 계속 전송하는건가요
아님 이미 전송된 클라이언트의 수신버퍼의 내용을 클라이언트가 읽는걸까요
이상한점은 서버의 전송버퍼에 남아있는 데이터를 전송하는거라고 한다면
클라이언트가 null값이 아닌 정상적인 데이터를 받아야 하는게 아닌가요?
마찬가지로 클라이언트의 수신버퍼에 남아있는 데이터를 읽어오는것이라해도
정상적인 데이터의 값이 나와야 할것 같은데
왜 null값을 계속 받아오는 건지..
도통 이해가 되질 않습니다.
제발 조언 부탁드립니다
혹시나해서 소스 첨부해보겠습니다.
=================================================================
/*******************************************************************************
ClientManager.c
*******************************************************************************/
//========================헤더파일 로드.... & 상수값 설정 ===================================
#include "Common.h"
#include
#include
#include
#include
#include
#include
#include
#define BUFSIZE 1024
#define WRITE_BUF_SIZE 100
//=====================================================================================
//================================전역 변수 선언 ========================================
char Buf[BUFSIZE];
int serverSock;
struct timeval timeout;
//=====================================================================================
//===========================시그날 핸들러 처리 함수 ======================================
void signal_handler(int signo)
{
// do...... something...
RuntimeLog("*** Client Out -> Broken Pipe... ");
}
//=====================================================================================
//====================================메인 함수 =========================================
main()
{
//=========================변수선언========================
int clientSock, sockfd;
int client[FD_SETSIZE];
int i, clientCnt, msgLen;
int fd_num, maxfd;
char smsg[PACKET_SIZE];
fd_set readfds, allfds;
int iTest=0;
int rRst=0;
time_t time_now;
//=========================================================
//TEST 전문 내용.... ------------------------------
int wi = 0;
int nBufCnt = 0;
char writeBuf[WRITE_BUF_SIZE];
char testBuf[5000];
unsigned char jun[] =
"010000050060005000400030"
"0000000100005002"
"0000110002100040000007500500035001001010010100100000000000000010000000050000000300000003"
"00000001000100010000000020002000100000000200030001000000002000400010"
"000000000000000000000000020100224105601127-0-0-1 "
"000000000000000000000000020100224105601127-0-0-1 "
"00000001PROCESS명 00000012010022410560100:00:30030015201002241056010"
"000000010001V1 F1 201002241056010001"
"0000120003100010000007500500035001001010010000000000000000000020000000100000000500000002"
"00000003000100010"
"000000000000000000000000020100224105601127-0-0-1 "
"000000000000000000000000020100224105601127-0-0-1 "
"00000003PROCESS명 00000012010022410560100:00:30030015201002241056010";
memset(testBuf, 0x00, sizeof(testBuf));
sprintf(testBuf, "%c%010d%s%c", 0x02, strlen(jun), jun, 0x03); // 1 + 10 + 689 + 1 = 701
// ---------------------------------------------------
//=======================시그널 핸들러 함수 호출.. ===========================
// signal(SIGPIPE, signal_handler); //SIGNAL 신호 핸들러..
signal(SIGPIPE, SIG_IGN); //SIGNAL 신호 무시..
//=======================================================================
//=======================shared memory 설정, 서버소켓 설정 ================
SetShMemory(SHR_ADDR);
serverSock = ServerSocketBind(9797);
fd_num = 0;
clientCnt = -1;
clientSock = serverSock;
maxfd = serverSock;
//=======================================================================
for(i=0; i < FD_SETSIZE; i++)
client[i] = -1;
FD_ZERO(&readfds);
FD_SET(serverSock, &readfds);
while(1)
{
RuntimeLog("====================================================");
// 1. Read Sared Memory
ReadShMemory(Buf);
// RuntimeLog("Read Shared Memory : %s", Buf);
time(&time_now);
// RuntimeLog(ctime(&time_now));
// 2. Select
// 3. 접속된 클라이언트가 있으면 SH Memory 내용을 Write
++iTest;
allfds = readfds;
timeout.tv_sec = 0; // 아래에서 sleep 을 설정
timeout.tv_usec = 1;
//=========SELECT ============ select 함수 설정
RuntimeLog("1) select .............. ");
fd_num = select(maxfd+1, &allfds, (fd_set *)0, (fd_set *)0, &timeout);
// fd_num = select(maxfd+1, &allfds, (fd_set *)0, (fd_set *)0, NULL);
if(fd_num < 0)
{
RuntimeLog("*** [ERROR] select ");
continue;
}
// if(fd_num == 0) continue;
// RuntimeLog(" fd_num=%d", fd_num);
for(i=0; i < FD_SETSIZE; i++)
{
if ( client[i] > -1 ) RuntimeLog(" client[%d]=%d", i, client[i]);
}
//============================
if(FD_ISSET(serverSock, &allfds))
{
//================ACCEPT===============
clientSock = accept(serverSock, (struct sockaddr *)0, (int *)0);
for(i = 0; i < FD_SETSIZE; i++)
{
if(client[i] <0)
{
client[i] = clientSock;
RuntimeLog(">>>>>>>>>> clientSock saved position=[%d] : clientSocknum = [%d]\n", i, clientSock);
break;
}
}
if(i==FD_SETSIZE)
{
RuntimeLog("*** Warning : Full Socket ");
close(sockfd);
client[i] = -1;
}
FD_SET(clientSock, &readfds);
if(clientSock > maxfd) maxfd = clientSock;
if(i > clientCnt) clientCnt = i;
if(--fd_num <= 0) continue;
}
//=========================================== 접속된 클라이언트에게 브로드캐스팅
for(i=0; i<=clientCnt; i++)
{
if((sockfd = client[i]) < 0) continue;
RuntimeLog("2) write.........connected client count=%d", clientCnt+1);
RuntimeLog(" - client[%d]=%d", i, sockfd);
RuntimeLog(" - fd_num=%d", fd_num);
nBufCnt = (strlen(testBuf) / WRITE_BUF_SIZE) ;
//=========================================================
if( (strlen(testBuf) % WRITE_BUF_SIZE) != 0 ) ++nBufCnt;
RuntimeLog(" > nBufCnt=[%d]", nBufCnt);
for(wi=0; wi < nBufCnt; wi++)
{
memset(writeBuf, 0x00, WRITE_BUF_SIZE);
memcpy(writeBuf, testBuf+(WRITE_BUF_SIZE*wi), WRITE_BUF_SIZE );
RuntimeLog(" >%2d's [%s]", wi, writeBuf);
// WRITE...
write(sockfd, writeBuf, WRITE_BUF_SIZE);
}
//==========READ================= 종료된 클라이언트 검사
if(FD_ISSET(sockfd, &allfds))
{
memset(smsg, 0x00, PACKET_SIZE);
if(msgLen = read(sockfd, smsg, PACKET_SIZE)<=0)
{
close(sockfd);
FD_CLR(sockfd, &readfds);
client[i] = -1;
}
}
}
//===========================================
// 4. 3초 sleep
sleep(3);
}
}
==============================================================================
문제가 있는 쪽은?
올리신 것은 서버측 소스 같군요. SIGPIPE 까지 처리하셨으니 소켓이 문제가 되었을 것 같진 않네요
커널차원까지 가다보면 소켓이 닫힐 때 문제를 일으킬 확률이 0% 라고는 못하지만...
의심이 가는 것은 클라이언트 측 아닐까 궁금하네요 null 데이터를 계속 받는 다고 하셨는데
소켓 접속이 끊어졌다면 block 이 되던가 close 처리가 되어야 할텐데 0 이라는 데이터를 계속 받는 거라면...
소켓 버퍼에서 무조건 데이터를 읽도록 클라이언트 측 처리가 잘못되었을 가능성이 있습니다...
-----
안녕하세요 소프트웨어 공학센터 장원석 책임입니다.
http://www.software.kr
댓글 달기