IT_Programming/Network Programming

[Linux C] 에코 서버, 에코 클라이언트 만들기 (sinal intterrupt 적용)

JJun ™ 2009. 6. 30. 10:58

==================================================================================================

 

interrupt signal service routin으로 클라이언트의 비정상적인 종료를 서버에서 확인하는 간단한 예제..

얼마든지 응용해서 다양한 signal interrupt 에 대한 처리를 할 수 있게 만든 예제 코드 (TCP/IP)

 

                                                       참고 : 윤성우의 TCP/IP 프로그래밍

==================================================================================================

 

[실행화면]

 

- Echo_Server : 서버는 계속 돌아야 한다....

 

- Echo_Client 

 

 

=================================================================================================

 

[소스코드]

/**************************************************************/

/*           내   용 : 에코 서버                                                      */

/*           작성자 : creazier (블로그 주인장)                                 */

/*           블로그 : http://blog.daum.net/creazier                         */

/**************************************************************/

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <sys/wait.h>
#include <signal.h>

 

#define BUFSIZE  1024

 

void signal_init(struct sigaction *zombi, struct sigaction *act, int *state1, int *state2);
void handler(int signal);
void conn_init(int *serv_socket, struct sockaddr_in *serv_addr, int port);
void error_handling(char *message);

 

int main(int argc, char *argv[])
{
      int serv_sock, clnt_sock, clnt_addr_size, str_len, state1, state2;
      struct sockaddr_in serv_addr, clnt_addr;
      struct sigaction act, zombi_exit;
      char message[BUFSIZE];
      pid_t pid;
 
      if(argc != 2) // 메인함수 인자 확인 및 사용법 출력
      {
            printf("Usage : %s <port>\n", argv[0]);
            exit(1);
      }

     

      signal_init(&zombi_exit, &act, &state1, &state2);
      if(state1 != 0 && state2 != 0) // 시그널 세팅 확인
            error_handling("sigaction() Error!");
   
      memset(&serv_addr, 0, sizeof(serv_addr));
      conn_init(&serv_sock, &serv_addr, atoi(argv[1]));

 

      if(bind(serv_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1)
            error_handling("bind() Error!");

      

     if(listen(serv_sock, 5) == -1)
            error_handling("listen() Error!");
 
      while(1)
      {
            clnt_addr_size = sizeof(clnt_addr);
            clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
 
            if(clnt_sock == -1)
                  continue;
  
            if((pid = fork()) == -1) // 멀티 태스킹 (멀티 프로세스)이 제대로 이뤄졌는지 확인
            {
                  close(clnt_sock);
                  continue;
            }
  
            if(pid > 0) // 부모 프로세스
            {
                  puts("연결 생성!");
                  close(clnt_sock);
                  continue;
            }
            else // 자식 프로세스
            {
                  close(serv_sock);
  
                  while((str_len = read(clnt_sock, message, BUFSIZE))  !=  0)
                  {
                        write(clnt_sock, message, str_len);

                        if(!strcmp(message, "SIGNAL_INTERRUPT")) // 클라이언트 인터럽트 메시지를 확인하고
                        {
                              raise(SIGTSTP); // 자기 자신에게 인터럽트를 건다.
                              break;
                        }
  
                        write(1, message, str_len);
                  }

                  puts("연결 종료!");
                  close(clnt_sock);
                  exit(0);
            }
      }

      return 0;

}

 

void signal_init(struct sigaction *zombi, struct sigaction *act, int *state1, int *state2)
{ // 시그녈 관련 처리 세팅
      act -> sa_handler = handler; // 인터럽트 서비스 루틴 등록
      sigemptyset(&(act->sa_mask));
      act -> sa_flags = 0;

      *state1 = sigaction(SIGTSTP, act, 0);

 
      zombi -> sa_handler = z_handler; // 인터럽트 서비스 루틴 등록 (좀비 프로세스 해결)
      sigemptyset(&(zombi->sa_mask));
      zombi -> sa_flags = 0;

      *state2 = sigaction(SIGCHLD, act, 0);
}

 

void z_handler(int signal) // 좀비 프로세스 해결 (반복문 돌려서 wait() 시키는 것의 대안)

{

pid_t pid;

int rtn;

pid = waitpid(-1, &rtn, WNOHANG);

WEXITSTATUS(rtn);

}

 

void handler(int signal) // 인터럽트 서비스 루틴
{
      printf("클라이언트가 비정상적으로 종료되었습니다.\n");
     //  exit(0); // 서버는 종료되서는 안되므로....
}

 

void conn_init(int *serv_socket, struct sockaddr_in *serv_addr, int port) // 주소 정보 초기화
{
      *serv_socket = socket(PF_INET, SOCK_STREAM, 0);
      serv_addr->sin_family = AF_INET;
      serv_addr->sin_addr.s_addr = htonl(INADDR_ANY);
      serv_addr->sin_port = htons(port);
}

 

void error_handling(char *message) // 에러 처리 함수
{
      fputs(message, stderr);
      fputc('\n', stderr);
      exit(1);
}

 

-------------------------------------------------------------------------------------------------

 

/**************************************************************/

/*           내   용 : 에코 클라이언트                                             */

/*           작성자 : creazier (블로그 주인장)                                 */

/*           블로그 : http://blog.daum.net/creazier                         */

/**************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>

 

#define BUFSIZE  1024

 

void signal_init(struct sigaction *act, int *state);
void signal_handler(int signal);
void conn_init(int *clnt_sock, struct sockaddr_in *serv_addr, const char *IP_Address, const int port);
void error_handling(char *message);

 

static int clnt_sock;

 

int main(int argc, char *argv[])
{
      int str_len, state;
      struct sockaddr_in serv_addr;
      struct sigaction act;
      char message[BUFSIZE];

 

      if(argc != 3) // 메인함수 인자 및 사용법 출력
      {
            printf("Usage : %s <IP> <PORT>\n", argv[0]);
            exit(1);
      }

     

      signal_init(&act, &state);
      if(state != 0) // 시그널 관련 세팅이 제대로 됐는지 확인
            error_handling("sigaction() Error!");
  
      memset(&serv_addr, 0, sizeof(serv_addr));
      conn_init(&clnt_sock, &serv_addr, argv[1], atoi(argv[2]));

     

      if(connect(clnt_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
            error_handling("connect() Error!");
  
      while(1)
      {
            fputs("전송할 메시지를 입력하세요 (q = Quit) : ", stdout);
            fgets(message, BUFSIZE, stdin);
    
            if(!strcmp(message, "q\n")) // q를 입력하면 클라이언트의 정상적인 종료
                  break;

          

            write(clnt_sock, message, strlen(message));

            str_len = read(clnt_sock, message, BUFSIZE-1);
            message[str_len] = 0;

            printf("서버로부터 전송되 메시지 : %s\n", message);
      }

      close(clnt_sock);
 
      return 0;
}

 

void signal_init(struct sigaction *act, int *state) // 시그녈 관련 처리 세팅
{
      act -> sa_handler = signal_handler; // 인터럽트 서비스 루틴 등록
      sigemptyset(&(act->sa_mask));
      act -> sa_flags = 0;

      *state = sigaction(SIGTSTP, act, 0);
}

 

void signal_handler(int signal) // 인터럽트 서비스 루틴
{
      char *signal_message = "SIGNAL_INTERRUPT";
 
      printf("인터럽트 시그널 발생! 비정상으로 종료됩니다.\n");
      write(clnt_sock, signal_message, strlen(signal_message));
      close(clnt_sock);
      exit(1);
}

 

void conn_init(int *clnt_sock, struct sockaddr_in *serv_addr, const char *IP_Address, const int port)
// 주소 정보 초기화
      *clnt_sock = socket(PF_INET, SOCK_STREAM, 0);
      serv_addr -> sin_family = AF_INET;
      serv_addr -> sin_addr.s_addr = inet_addr(IP_Address);
      serv_addr -> sin_port = htons(port);
}

 

void error_handling(char *message) // 에러 처리 함수
{
      fputs(message, stderr);
      fputc('\n', stderr);
      exit(1);
}

=================================================================================================