select함수 질문요 ^^:
void Tcp_test(void)
{
int retval, res;
int maxfdp;
fd_set rfds;
struct timeval tv;
char *start = "send test OK!!";
char buffer[100] ={0, };
maxfdp = fd_sock + 1;
FD_ZERO(&rfds);
//5초기다린다
tv.tv_sec =5;
tv.tv_usec =0;
/* if(res = recv(fd_sock, buffer, sizeof(buffer), 0)){
buffer[res] = '\0';
printf("msg_peek :[%s]\n", buffer);
}*/
// while(1){
FD_SET(maxfdp, &rfds);
if(retval = select(maxfdp, &rfds, NULL, NULL, &tv) < 0) {
printf("select error\n");
exit(0);
}
if (FD_ISSET(maxfdp, &rfds)) {
FD_CLR(fd_sock, &rfds);
close(fd_sock);
OpenTcp();
/* if(res = recv(fd_sock, buffer, sizeof(buffer), 0)){
buffer[res] = '\0';
printf("msg_peek :[%s]\n", buffer);
}
if(send(fd_sock, buf_kp, strlen(buf_kp), 0) == -1){
perror("tcp send failed");
exit(1);
}
printf("send data: [%s]\n", buf_kp);
*/
}
else{
// memset(buf_kp, 0, sizeof(buf_kp));
// memcpy(buffer, buf_kp+7, 5);
// buffer[5] = '\0';
if(send(fd_sock, buf_kp, strlen(buf_kp), 0) == -1){
perror("tcp send failed");
exit(1);
}
printf("send data: [%s]\n", buf_kp);
// FD_CLR(fd_sock, &rfds);
// close(fd_sock);
// sleep(2);
// OpenTcp();
// printf("re connect\n");
// exit(0);
}
// }
}
-
지금셀렉함수 이용해서 send recv하고 서버에서 반응이 없거나 서버가 죽었으면 클라이언트쪽에서 재접속을 어떻게 해야 하나요?
일주일째...진도는 안나가구 답답하네요..초짜라서 책봐도 잘모르겠구.
tcp 생성하고 오픈은 메인에서하고 프로세스생성 해서 위에 있는 함수를 돌렸거든요...하위단은 485로 데이터를 받아오고 TCP재접속에대해 설명좀..예제라도
부탁드려요 ㅡㅡ;
이렇게 하면 되지않을까여?
client 소스
서버가 죽거나 응답을 안 한다면 클라이언트가 할 수 있는 일은,연결이
서버가 죽거나 응답을 안 한다면 클라이언트가 할 수 있는 일은,
연결이 아직도 있는 상태라면 해당 연결을 끊고
같은 곳이나 다른 곳으로 연결을 재시도해 보거나
업무로직에 정의되어 있는 장애처리 루틴을 밟는 일 외에는 거의 없습니다.
말씀하신 TCP 재접속이라는 것은 socket API 상으로는 없습니다.
해당 서버를 이용하지 못했을 때 어떻게 하겠다는 정책과 이를
사용자 코드를 이용해서 구현해 주는 것이지요.
어떻게 할지는 먼저 요구사항을 정해야겠죠.
한번까지는 에러발생없이 재시도를 하고, 그래도 실패하면
실패했다는 로그를 남기고 다른 서버로 연결을 시도하고,
다른 서버에서도 실패를 한다면 해당 작업을 포기하고
그 다음 절차를 밟는다 식으로요.
예를들어, 만일 제공하려는 서비스가 예를들어 인증서비스였고,
인증서버가 사용자정보를 위해서 사용자정보서버에 클라이언트로 붙어서
사용자 데이터를 가져와야 하는 경우라면,,
사용자정보서버를 두개를 두고,
한 쪽 서버가 죽으면 다른 쪽 서버로 바꾸어서 접속을 시도하도록 하고,
사용자 정보서버 현황은 프로그램 시동시에 아규먼트로 받거나
아니면 컨피그 파일에 등록해 놓도록 하고,
두 사용자정보서버가 모두 다 제대로 동작을 안 한다면 그때는
사용자 인증없이 그대로 사용을 하도록 한다든지
(이렇게 되면 사업자가 손해를 보겠죠)
아니면 서비스 제공을 거부한다든지 하고(이러면 콜센터 불나겠죠?),
인증서버가 살았는지 확인하는 별도의 스레드를 시동시켜서
서버가 살아나면 곧바로 서비스가 재개되도록 할 수 있겠죠.
아니면 무식하게 될때까지 계속 접속을 시도하든지요. (이러면 인증시스템 먹통되겠죠?)
일단 클라이언트가 서버에 접속해서 10초안에 요청을 날리고
응답을 받는 작업을 하는 녀석이라고 정의를 하도록 하죠.
그럼 함수들을 정의해 보도록 하죠.
첫번째는 재접속 업무로직이 들어가있는 함수들:
get_user_info(char *id, USERINFO *info)
성공: OK
실패: 에러코드
get_info_server()
성공: 소켓
실패: 에러코드
두번째는 재처리 업무로직이 없이 소켓에만 관련된 함수들:
(이 경우에는 처리시간 초과를 고려하기 위해서 timeout들을 생각해 주어야 합니다.)
connect_server_to() : timeout이 있는 connect
writen_to() : timeout이 있는 write
readn_to() : timeout이 있는 read
이 함수에서는 접속장애는 get_info_server() 안에서 재시도 한다고 가정을 했고,
read, write 실패시에는 현재 소켓을 닫고 재시도 루틴으로 가도록 했습니다.
재시도루틴은 결국 get_info_server() 안에 들어있습니다.
이를 read, write 시마다 nretry를 증가시키고
retry: 에서 nretry 횟수를 검사해서 일정정도 횟수가 증가하면 실패하도록 변경할 수도 있겠습니다. (아래 코드)
이 경우에는 get_info_server()에 들어있던 재시도 루틴이 get_user_info() 쪽으로 들어온것이고요.
get_info_server() 는 서버에 연결하는 역할을 하며,
설계에 따라 재처리로직을 둘 수도 있고, 그렇지 않을 수도 있습니다.
접속할 서버 리스트가 여러개 있다면 아래처럼 서버리스트를 시도해 볼 수 있을 것이고요,
이 코드처럼 하나의 서버에 성공하기전까지 NRETRY만큼 재시도하도록 할 수 있겠고,
서버로 연결이 가능한지를 알아본 후(is_primary_server_ok())
연결시도가 실패하면 해당 연결을 사용못한다고 표시(mark_primary_server(FAIL))해 놓을 수도 있을것입니다.
서버가 연결되지 않으면 특정위치에 primary.fail 이라는 파일을 생성해 둔다고
약속을 하면, is_primary_server_ok()에서는 해당 파일이 있는지를 확인하고,
mark_primary_server(FAIL)에서는 해당 파일을 생성하는 식으로 구현을 할 수 있겠죠. 서버 연결이 정상화된다면 관리자가 수작업으로 해당 파일을 삭제한다거나 아니면 별도의 감시 스레드가 주기적으로 연결검사를 해서 이 작업을 수행하도록 할 수 있겠고요.
이 코드에서는 매번 get_user_info() 마다 연결을 맺고,
처리가 끝나면 연결을 끊었는데,
long-lived TCP를 사용해서 한번 열어놓은 소켓은 연결이 깨지지 않는 한
계속사용할 수 있도록 하는 것도 가능하겠죠.
get_info_server() 와 close()를 통해서 소켓을 열고, 닫고 했는데,
get_info_server()와 release_info_server() 두 개로 나누어서
커넥션 풀에서 연결을 가져오고, 사용 후 반납하는 구조로 가져가도 되겠습니다.
그리고 int sd 로 연결을 표현했는데 struct CONN {} 등으로 연결을 추상화시킬 수도 있을 것이고,
주기적으로 연결상태를 점검하는 스레드와, 연결이 손상되었을 때 해당 연결을 복구시켜주는 스레드를 두어서 관리할 수도 있을 것입니다.
두번째는 재처리 업무로직이 없이 소켓에만 관련된 함수들을 생각해 보도록 하지요.
o connect
소켓 API의 connect는 타임아웃을 지정할 수 있는 방법이 없으므로
보통 wrapping해서 사용합니다.
Stevens 책의 15.3, 15.4 장을 보면 소켓을 non-blocking 모드로 바꾸어서
connect에 timeout을 거는 예제가 있으니 이를 참조하시면 됩니다.
아니면 socket, connect, timeout 으로 게시판에서 검색하셔도 되고요.
o write
write의 경우도 timeout을 고려해야겠지만, 많은 데이터가 아니면
버퍼에 쓰고 곧바로 리턴하며, 대부분 그 다음에 read를 수행하므로
오류상황이 발생하면 read시에 감지가능하므로 Stevens 책 3.9의
writen 정도를 쓰시면 되겠고요.
(테스트를 해 보느라고 이쪽에서 10메가를 write()로 쓰고 저쪽에서
안 받으니 write에서 block되는군요.
loop안에서 select를 사용하고 non-block 모드로 전환해서 write를 해야할까요? ㅡ.ㅡ)
o read
Stevens 책의 readn에서 read하기 전에 timeout걸린 select 를 수행하면
될 듯 합니다.
단, 이 코드는 플랫폼에 따라서 서버에서 독한 마음먹고 5초에 1바이트씩 보내면 5 * 읽을byte수 만큼 시간이 걸릴 수 있습니다.
앞서도 말씀드렸지만요구사항이 상황에 따라서 다 틀리니 그에 맞도록 적절하게 재처리로직을 수립하셔야 할 것이고요, 에러처리나 에러전달방법, 로깅수행 위치 등 여러가지도 함께 고려하셔야 할 것 같습니다.
요즘은 TCP/IP 프로그래밍과 이를 이용한 서버제작 등에 관한 책도 제법 많으니 차분하게 한번 읽어보시면 될 듯 싶습니다.
댓글 달기