select와 signal을 이용했을때 문제점.
글쓴이: cjy1126 / 작성시간: 금, 2005/02/18 - 3:04오후
select와 signal을 이용해서 서버를 작성하였습니다.
select만 있을때는 잘되는데, signal을 넣으니까 가끔 들어온 패킷을 무시하고, 링크드 리스트로 저장한 데이터가 꼬입니다.
flags = fcntl(listen_sock, F_GETFD, 0); fcntl(listen_sock, F_SETFD, flags |O_NONBLOCK);
[/code]
위와같이 하여서 Recv나 accept에서 block가 일어나지 않아야하는데, 가끔 block이 일어나는것 같기도 합니다
그런 상태에서 시그널에 의해 block이 풀리는것 같습니다.
쉽게 쓸수있는 윈도우의 SetTimer같은 함수가 없을까요?
아래는 소스입니다.(주석부분이 이상하게 제 에디트와 줄이 안맞네요. 에디트에서는 깔끔하게 정리했는데... 지저분해져서 죄송합니다.)
변수
extern SOCKETINFO *si_head, *si_tail; /*소켓구조체 링크드 리스트 */ struct itimerval timer; /* setitime용 시간 */ SOCKETINFO *GetNextSocketInfo(SOCKETINFO *prev); /* 리스트의 다음부분을 구하는 함수 */
sig_alarm
void sig_alarm(int signo) /* 알람 발생시... */
{
SOCKETINFO *si_temp;
PACKET packet;
printf("######################## sig alarm ##########################\n");
packet.type = PACKET_ISLIVE;
si_temp = si_head;
/* 리스트에 있는 socket들에 heartbeat 질의 */
while((si_temp=GetNextSocketInfo(si_temp)) != si_tail)
{
send(si_temp->socket, (char *)&packet, sizeof(packet), 0);
printf("send: si_temp->socket = %d\n", si_temp->socket);
}
setitimer(ITIMER_REAL, &timer, NULL); /* 타이머를 다시 셋팅 */
}
init: 데이터 초기화
void Init() /* 데이터 초기화 */
{
si_head = (SOCKETINFO *)malloc(sizeof(SOCKETINFO));
si_tail = (SOCKETINFO *)malloc(sizeof(SOCKETINFO));
si_head->prev = si_head;
si_head->next = si_tail;
si_tail->prev = si_head;
si_tail->next = si_tail;
timer.it_value.tv_sec = 10;
timer.it_value.tv_usec = 0;
timer.it_interval.tv_usec = 0;
timer.it_interval.tv_usec = 0;
}
main
int main()
{
int listen_sock, client_sock;
struct sockaddr_in listen_addr, client_addr;
int flags, result, addrlen;
PACKET packet;
SOCKETINFO *si_temp;
fd_set rset, wset;
int maxfd;
Init();
Ora_Connect("****", "****"); /* 오라클 연결 */
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sock<0)
ExitError("Listen Socket Create Failed");
#ifdef DEBUG
printf("Listen Socket Create Ok!\n");
#endif
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = INADDR_ANY;
listen_addr.sin_port = htons(PORT);
result = bind(listen_sock, (struct sockaddr *)&listen_addr, sizeof(listen_addr));
if(result<0)
ExitError("Bind Failed\n");
#ifdef DEBUG
printf("Bind Ok!\n");
#endif
result = listen(listen_sock, BACKLOG);
if(result<0)
ExitError("Listen Failed");
#ifdef DEBUG
printf("Listen Ok!\n");
#endif
flags = fcntl(listen_sock, F_GETFD, 0); /* listen을 비동기 소켓으로 전환 */
fcntl(listen_sock, F_SETFD, flags | O_NONBLOCK);
maxfd = listen_sock;
signal(SIGALRM, sig_alarm); /* 알람 시그널 등록 */
setitimer(ITIMER_REAL, &timer, NULL);
while(1)
{
FD_ZERO(&rset);
FD_SET(listen_sock, &rset);
si_temp = si_head;
/* 리스트에 데이터가 있다면 그 소켓을 FD_SET으로 등록 */
while((si_temp=GetNextSocketInfo(si_temp)) != si_tail)
{
printf("GetNextSocketInfo: si_temp->socket = %d\n", si_temp->socket);
FD_SET(si_temp->socket, &rset);
}
printf("befor select\n");
result = select(maxfd+1, &rset, 0, 0, 0); /* 셀렉트... 대기 */
if(result<0)
{
if(errno==EINTR)
{
printf("select: erron==EINTR\n");
continue;
}
ExitError("Select Failed");
}
printf("after select\n");
if(FD_ISSET(listen_sock, &rset)) /* listen 소켓에 접속요청이라면 */
{
client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addrlen);
if(client_sock<0)
{
ViewError("Accept Failed");
continue;
}
if(client_sock>maxfd)
maxfd = client_sock;
AddSocketInfo(client_sock); /* 소켓정보 리스트에 등록 */
flags = fcntl(client_sock, F_GETFD, 0); /* 접속받은 소켓을 비동기로 전환 */
fcntl(client_sock, F_SETFD, flags | O_NONBLOCK);
#ifdef DEBUG
printf("Accept Ok!\n");
#endif
}
si_temp = si_head;
/* 리스트에 있는 소켓 FD_ISSET으로 검사 */
while((si_temp=GetNextSocketInfo(si_temp)) != si_tail)
{
if(FD_ISSET(si_temp->socket, &rset))
{
memset(&packet, 0, sizeof(packet));
printf("before Recv\n");
result = Recv(client_sock, (char *)&packet, sizeof(packet), 0); /* Recv... */
if(result<0)
{
if(errno==EINTR) /* 시그널이라면 계속 */
continue;
else
{
SOCKETINFO *remove; /* 에러면 소켓리스트 정리 */
if(client_sock==maxfd)
--maxfd;
remove = si_temp;
si_temp = GetNextSocketInfo(si_temp);
RemoveSocketInfo(remove);
continue;
}
}
printf("after Recv\n");
if(packet.type==PACKET_START) /* 출근 */
{
printf("PACKET_START: si_temp->socket = %d\n", si_temp->socket);
OnStart(packet.sabun); /* 오라클에 출근시간 저장 */
}
else if(packet.type==PACKET_END) /* 퇴근 */
{
SOCKETINFO *remove;
printf("PACKET_END: si_temp->socket\n", si_temp->socket);
OnEnd(packet.sabun); /* 오라클에 퇴근시간 저장 */
remove = si_temp;
si_temp = GetNextSocketInfo(si_temp);
RemoveSocketInfo(remove); /* 소켓리스트에서 제거 */
continue;
}
else if(packet.type==PACKET_CHULJANG) /* 오라클에 출장 저장 */
OnChulJang(packet.sabun);
/* sig_alarm에서 보낸 PACKET_ISLIVE에 대한 응답 */
else if(packet.type==PACKET_LIVE)
{
printf("PACKET_LIVE: si_temp->socket = %d\n", si_temp->socket);
}
else
printf("잘못된값: si_temp->socket = %d\n", si_temp->socket);
}
}
}
return 0;
}
Forums:


Re: select와 signal을 이용했을때 문제점.
F_GETFL 과 F_SETFL 을 F_GETFD 와 F_SETFD 로 잘못 쓰신 듯 보입니다.
일단 고쳐보시고 다시 테스트해 보세요...
우엉....
리스트에 있는 데이터를 읽는 부분에서
result = Recv(client_sock, (char *)&packet, sizeof(packet), 0);울고싶네요.
client_sock 은 1개 짜리로 db랑 테스트 하던건데... 이걸 si_temp->socket로 안바꿔서 그랬네요. ㅠ.ㅠ
괜히 엄한 sig_alarm이나 탓하고... 제가 제대로 모르는건가하고... 모든걸 의심했네요.
그래도 덕분에 코드가 좀 더 튼튼해졌지만요.(모때문에 에러인지 몰라서 낌새 보이는건 모두 수정 ㅡㅡ;)
F_GETFD와 F_SETFD도 F_GETFL 와 F_SETFL로 고쳤습니다.
답변 감사드립니다. ^^
댓글 달기