[몇일째 흑흑 도와주세요] socket 프로그램에서...
select를 이용하여 간단한 메아리 서버를 만들었습니다.
하지만 클라이언트와 종료시 문제점이 있습니다.
클라이언트 하나만 연결시에는 괜찮지만 둘이상일때는 :
1 A클라이언트 접속 이상 무
2 B클라이언트 접속 이상 무
3.A클라이언트 종료 --> 이때 문제점이 발생 합니다.
문제점은 아직 연결되어 있는 B클라이언트가 입력을 하면 서버로 전송이 되지 않습니다. 하지만 A클라이언트가 다시 접속하면 그전에 B클라이언트가 전송하려다 실패한 메세지가 이때 전달됩니다.
그리고 클라이언트 접속 하고 종료한 만큼 TIME_WAIT가 생김니다.
제가 알기로는 금방 TIME_WAIT상태에서 없어져야 하는데 몇분후에야 없어짐니다. 밑에 내용은 4개의 클라이언트가 접속후 종료한 상태 입니다.
bash-2.03$ netstat -a | grep 7894
*.7894 *.* 0 0 24576 0 LISTEN
crs.35319 crs.7894 32768 0 32768 0 TIME_WAIT
crs.35320 crs.7894 32768 0 32768 0 TIME_WAIT
crs.35321 crs.7894 32768 0 32768 0 TIME_WAIT
crs.35322 crs.7894 32768 0 32768 0 TIME_WAIT
---- 서버 ----------
int main(int argc,char *argv[]) { int i,listenfd,connfd,sockfd; int maxfd,maxi,n,j; int nready,clilen,client[FD_SETSIZE]; fd_set rset,allset; char buf[MAXSIZE]; struct sockaddr_in cliaddr,servaddr; if((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("Can not open socket"); exit(1); } bzero((char *)&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0) { perror("error bind"); exit(1); } if(listen(listenfd,WQUEUE) < 0) { perror("error listen"); exit(1); } maxfd = listenfd; maxi = -1; for(i = 0;i < FD_SETSIZE;i++) client[i] = -1; FD_ZERO(&allset); FD_SET(listenfd,&allset); for(;;) { rset = allset; /*allset에서 설정된 bit를 rset으로 넘겨준다.*/ nready = select(maxfd + 1,&rset,NULL,NULL,NULL); /* return value */ if(FD_ISSET(listenfd,&rset)) /* client connection */ { clilen = sizeof(cliaddr); if((connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen)) < 0) { perror("error accept"); exit(1); } printf("connection clientt.... \n"); for(i = 0;i < FD_SETSIZE;i++) { if(client[i] < 0) { client[i] = connfd; break; } } if(i == FD_SETSIZE) perror("too many clients"); FD_SET(connfd,&allset); if(connfd > maxfd) maxfd = connfd; if(i > maxi) maxi = i; } for(i = 0;i <= maxi;i++) /* request client search */ { if((sockfd = client[i]) < 0) continue; if(FD_ISSET(sockfd,&rset)) /* client communication */ { switch((n = recv(sockfd,buf,MAXSIZE,0))) { case -1: case 0: close(sockfd); FD_CLR(sockfd,&rset); client[i] -1; break; default : printf("Recv Message : %s , Size : %d byte\n",buf,n); n = send(sockfd,buf,n,0); printf("Send Message : %s , Size : %d byte\n",buf,n); } } } } }
-------클라이언트---------------------------------
int main(int argc,char *argv[]) { int serverfd,maxfd; struct sockaddr_in server; fd_set rset; char buf[MAXSIZE]; int stdineof,n,ch; /* stdineof is check EOF of file*/ if((serverfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { perror("can not open socket"); exit(1); } bzero((char *)&server,sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr.s_addr = inet_addr(argv[1]); if(connect(serverfd,(struct sockaddr *)&server,sizeof(server)) < 0) { perror("error connect :"); exit(1); } maxfd = serverfd; FD_ZERO(&rset); stdineof = 0; while(1) { if(stdineof == 0) FD_SET(fileno(stdin),&rset); /*file not EOF*/ FD_SET(serverfd,&rset); if(!select(maxfd +1,&rset,NULL,NULL,NULL)) { perror("error select"); exit(1); } if(FD_ISSET(serverfd,&rset)) { int n; if((n = recv(serverfd,buf,MAXSIZE,0)) <= 0) { if(stdineof == 1) return 0; else { perror("error recv"); exit(1); } } printf("recv message from server: %s ,size = %d\n",buf,n); } if(FD_ISSET(fileno(stdin),&rset)) { n = 0; while((ch = getchar()) != '\n') *(buf + n++) = ch; *(buf + n) = '\0'; if(!strlen(buf)) /* if file EOF */ { stdineof = 1; /* file EOF */ shutdown(serverfd,SHUT_WR); /* close write */ FD_CLR(fileno(stdin),&rset); continue; } else { n = send(serverfd,buf,strlen(buf)+1,0); printf("send date %s, size = %d\n",buf,n); } } } }
select 하기위해서 FD_ZERO ,FD_SET 등의 초기화는 for
select 하기위해서 FD_ZERO ,FD_SET 등의 초기화는 for(;;) 안에서 매번 하세요.
아참, printf 를 믿지마세요. 화면에 바로 뿌리지 않고 버퍼링 됩니다.
printf("메세지\n"); 한 후 즉시 flush(stdout); 처럼 곧바로 내용을 화면에 뿌리도록 하시든지...
내 자식들도 나처럼 !!
printf는 line buffering이기때문에 메세지 마지막에 '\n
printf는 line buffering이기때문에 메세지 마지막에 '\n'가 있다면 fflush(stdout); 가 필요없는걸로 압니다.
^^
unix 에서는 개행문자가 있더라도 해줘야 할 때가 있더군요..
서버에서 소켓을 close 하실때 [code:1]case 0
서버에서 소켓을 close 하실때
이 부분을..
이렇게 바꿔 주세요.. :wink:
잠시 fd set 을 혼동하셨나보네욤..
printf로 디버깅 하실려면.. setbuf(stdout, 0);이
printf로 디버깅 하실려면.. setbuf(stdout, 0);
이걸 사용해 보시는것도 괜찮을꺼 같네요.
앞마당 먹고 시작한 저그의 8할은 뮤탈 테크를 먼저 탄다. 하지만 나머지 2할때문에 항상 스켄이 모자란다. - _-;
댓글 달기