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로 고쳤습니다.
답변 감사드립니다. ^^
댓글 달기