본문 바로가기
전공

[IT/Programing] 서버와 클라이언트를 이용한 채팅 하기

by GoLook 2011. 11. 10.
728x90




유닉스상에서 실행하였습니다.




1.chat_server.c

 
/*---------------------------------------------------------------------------------------------- 
파일명 : chat_server.c     

  기   능 :  채팅  참가자  관리,  채팅  메시지  수신  및  방송  

  컴파일  : gcc -o chat_server chat_server.c readline.c -lsocket -lnsl 

  실행예  : chat_server 6546

-----------------------------------------------------------------------------------------------*/ 

#include   <stdio.h> 

#include   <fcntl.h> 

#include   <stdlib.h> 

#include   <signal.h> 

#include   <sys/socket.h> 

#include   <sys/file.h> 

#include   <netinet/in.h> 

 

#define MAXLINE    1024 

#define MAX_SOCK    512 

  

char *escapechar = "exit\n"; 

int readline(int, char *, int); 

 

int main(int argc, char *argv[])    { 

   char   rline[MAXLINE], my_msg[MAXLINE]; 

   char   *start = "대화방에  오신걸  환영합니다...\n"; 

   int  i, j, n; 

   int  s, client_fd, clilen; 

   int  nfds;      /*  최대  소켓번호 +1 */ 

   fd_set  read_fds;    /*  읽기를  감지할  소켓번호  구조체 */ 

   int  num_chat = 0;    /*  채팅  참가자  수 */ 

   /*  채팅에  참가하는  클라이언트들의  소켓번호  리스트 */ 

   int  client_s[MAX_SOCK]; 

   struct sockaddr_in  client_addr, server_addr; 

    

   if (argc < 2)  { 

      printf("실행방법 :%s  포트번호\n",argv[0]);  

      return -1; 

   } 

    

   printf("대화방  서버  초기화  중....\n"); 

 

   /*  초기소켓  생성 */ 

      if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)    { 


      printf("Server: Can't open stream socket.");    

      return -1; 

   } 

    

   /* server_addr  구조체의  내용  세팅 */ 

   bzero((char *)&server_addr, sizeof(server_addr));   

   server_addr.sin_family = AF_INET;               

   server_addr.sin_addr.s_addr = htonl(INADDR_ANY); 

   server_addr.sin_port = htons(atoi(argv[1]));      

    

      if (bind(s,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0) { 

      printf("Server: Can't bind local address.\n"); 

      return -1; 

   } 

    

   /*  클라이언트로부터  연결요청을  기다림 */ 

   listen(s, 5); 

 

      nfds = s + 1;   /*  최대  소켓번호 +1 */ 

   FD_ZERO(&read_fds); 

    

   while(1) { 

      /* (최대  소켓번호 +1)  값을  갱신 */ 

      if ((num_chat-1) >= 0)  nfds = client_s[num_chat-1] + 1; 

 

      /*  읽기  변화를  감지할  소켓번호를 fd_set  구조체에  지정 */ 

      FD_SET(s, &read_fds); 

      for (i=0; i<num_chat; i++)  FD_SET(client_s[i], &read_fds); 

       

      /*---------------------------------- select()  호출 ------------------------------------- */ 

            if (select(nfds, &read_fds, (fd_set *)0, (fd_set *)0,(struct timeval *)0) < 0) { 

       printf("select error\n"); 

       return -1; 

      } 

      /*--------------------------  클라이언트  연결요청  처리 --------------------------- */ 

      if (FD_ISSET(s, &read_fds)) { 

       clilen = sizeof(client_addr); 

            client_fd = accept(s, (struct sockaddr *)&client_addr, &clilen); 

 

       if (client_fd != -1)  { 

        /*  채팅  클라이언트  목록에  추가 */ 

        client_s[num_chat] = client_fd;  

        num_chat++; 

        send(client_fd, start, strlen(start), 0); 

        printf("%d 번째  사용자  추가.\n",num_chat); 

       } 

      } 

      /*-  임의의  클라이언트가  보낸  메시지를  모든  클라이언트에게  방송 - */ 


      for (i = 0; i < num_chat; i++)  { 

      if (FD_ISSET(client_s[i], &read_fds)) { 

         if ((n = recv(client_s[i], rline, MAXLINE,0))  > 0)  { 

           rline[n] = '\0'; 

 

           /*  종료문자  입력시  채팅  탈퇴  처리 */ 

           if (exitCheck(rline, escapechar, 5) == 1) { 

         shutdown(client_s[i], 2); 

         if(i != num_chat-1)  client_s[i] = client_s[num_chat-1]; 

         num_chat--; 

         continue; 

           } 

 

            /*  모든  채팅  참가자에게  메시지  방송 */   

            for (j = 0; j < num_chat; j++)  send(client_s[j], rline, n, 0); 

            printf("%s", rline); 

         } 

      } 

   } 

 }

/* -------------------------------  종료문자  확인  함수 ----------------------------  

exitCheck()는  다음의  세  개의  인자를  필요로  한다 

 rline:  클라이언트가  전송한  문자열  포인터 

 escapechar:  종료문자  포인터 

 len:  종료문자의  크기 

---------------------------------------------------------------------------------------------*/ 

int exitCheck(rline, escapechar, len) 

  char  *rline;    /*  클라이언트가  전송한  메시지 */ 

  char  *escapechar;  /*  종료문자 */ 

  int    len; 

  { 

     int  i, max; 

     char  *tmp; 

    

     max = strlen(rline);   

     tmp = rline; 

     for(i = 0; i<max; i++) { 

        if (*tmp == escapechar[0]) { 

          if(strncmp(tmp, escapechar, len) == 0) 

          return 1; 

        } else  

  tmp++; 

     }  

   return -1; 









컴파일을 하시구요.
컴파일 하시는 방법은
gcc -o 사용할이름(보통 .c파일과 같은 이름) chat_server.c... 아래에 재정리
gcc -o chat_server chat_server.c readline.c -lsocket -lnsl 



오류가 없으면 chat_server가 생성됩니다.
 
(그 전에 readline.c라는 파일이 먼저 있어야 합니다!!)
 

 
readline.c

int readline(int fd, char *ptr, int maxlen) {

   int n, rc;

   char c;

      for(n = 1; n < maxlen; n++) {

  if((rc = read(fd, &c, 1)) == 1) {

     *ptr++ = c;

        if (c == '\n') break;

  } else if (rc == 0) {

        if(n == 1) return (0);

     else break;

 }

   }

   *ptr = 0;

   return (n);

} 





그럼 이제 채팅 방을 만들어 볼까요^^?
"chat_server 방번호" 를 입력하시면 됩니다

 




초기 대기 상태가 되었습니다.
자, 이제 클라이언트로 접속을 해야겠죠?ㅎㅎ

 
2. chat_client.c

/*-----------------------------------------------------------------------------------------  

파일명 : chat_client.c  

기  능 :  서버에  접속한  후  키보드의  입력을  서버에  전달하고,  

         서버로부터  오는  메시지를  화면에  출력한다.  

컴파일  : cc -o chat_client chat_client.c readline.c -lsocket -lnsl   

실행예  : chat_client 203.246.1.xxx 6546  사용자_ID  

-------------------------------------------------------------------------------------------*/  

#include <stdio.h>   

#include <fcntl.h>   

#include <stdlib.h>   

#include <sys/socket.h>   

#include <netinet/in.h>   

#include <sys/time.h>   

 

#define MAXLINE 1024   

#define MAX_SOCK 512   

char *escapechar = "exit\n";   

int readline(int, char *, int);   

 

int s;      /*  서버와  연결된  소켓번호 */  

struct Name {   

    char n[20];  /*  대화방에서  사용할  이름 */  

    int len;  /*  이름의  크기 */  

} name;   

 

int main(int argc, char *argv[]) {   

    char line[MAXLINE], sendline[MAXLINE+1];  

    int n, pid, size;  

    struct sockaddr_in server_addr;  

    int nfds;  

    fd_set read_fds; 

 

        if( argc < 4 ) {   

 printf("실행방법 : %s  호스트 IP 주소  포트번호  사용자이름 \n", argv[0]);  

 return -1;  

    }  

 

    /*  채팅  참가자  이름  구조체  초기화 */  

    sprintf(name.n, "[%s]", argv[3]);  

    name.len = strlen(name.n);  

 

    /*  소켓  생성 */  

        if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {   

          printf("Client : Can't open stream socket.\n");   

 return -1;  

    }  

 

    /*  채팅  서버의  소켓주소  구조체 server_addr  초기화 */  


    bzero((char *)&server_addr, sizeof(server_addr));  

    server_addr.sin_family = AF_INET;  

    server_addr.sin_addr.s_addr = inet_addr(argv[1]);  

    server_addr.sin_port = htons(atoi(argv[2]));  

 

    /*  연결요청 */  

        if(connect(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {   

  printf("Client : Can't connect to server.\n");   

 return -1;  

    } else {  

 printf("접속에  성공했습니다..\n");  

    }  

 

    nfds = s + 1;  

    FD_ZERO(&read_fds);  

 

    while(1) {  

    /* -------------------------------------- selelct()  호출 ---------------------------------------*/  

 FD_SET(0, &read_fds);  

 FD_SET(s, &read_fds); 

  if(select(nfds, &read_fds, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0) {   

      printf("select error\n");  

      return -1;  

 }  

 

    /*-------------------------  서버로부터  수신한  메시지  처리 -------------------------*/  

  if (FD_ISSET(s, &read_fds)) {   

      char recvline[MAXLINE];  

      int size;  

          if ((size = recv(s, recvline, MAXLINE, 0)) > 0) {   

    recvline[size] = '\0';   

    printf("%s \n", recvline);   

      }  

 }  

 

    /* ---------------------------------  키보드  입력  처리 ----------------------------------*/  

      if (FD_ISSET(0, &read_fds)) {   

          if (readline(0, sendline, MAXLINE) > 0) {   

       size = strlen(sendline);  

    sprintf(line, "%s %s", name.n, sendline);   

    if (send(s, line, size + name.len, 0) != (size+name.len))   

            printf("Error : Written error on socket.\n");   

    if (size == 5 && strncmp(sendline, escapechar, 5) == 0) {   

        printf("Good bye.\n");  

        close(s);  

        return -1;  

  }  

      }  

  } /* end of  키보드  입력  처리 */  

        } /* end of while() */   







chat_client.c 또한 컴파일 해줘야겠죠?
컴파일하셔서 오류 없으시면  chat_client가 생성됩니다.



설레는 마음으로 채팅하러 꼬꼬고~
 


접속 방법은,
chat_client [ip주소] [port] [채팅이름명] 

정상적으로 접속하시면
위와 같이 나오겠죠??
 






서버는 1명 "홍길동" 한분이 추가 되었기 때문에 
사용자 추가 메시지를 띄우게 됩니다.
 
 




채팅 방에 아무도 없네요..
(당연히 없습니다.ㅋㅋㅋㅋ) 
여러 창을 띄워서 같은 채팅방에 들어가셔서 시험 해보세욤!!
학교와 같이 같은 서버에 접속할 경우 다른 친구들이 만든 채팅방에 
들어가셔서 채팅을 할수 있습니다.

그럼 안뇽!! 


 
728x90