리눅스 채팅프로그램 좀 봐주세요
글쓴이: cutecat / 작성시간: 수, 2003/02/26 - 9:44오전
리눅스에서 채팅프로그램을 만들었는데....실행하니깐..서로 메세지교환이 안되고 접속이 그냥 끊겨버려여...
어디가 잘못된것인지...답변부탁드립니다.
================chat_server.c=====================
#include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <fcntl.h> #define maxline 1024 char *escapechar="exit\n"; int main(int argc,char *argv[]) { int s,client_fd,clilen; int num=0,i,j,n; int nfds; char sendline[maxline],rline[maxline],*buf; int client_s[512]; fd_set read_fds;//읽기감지구조체 struct sockaddr_in client_addr,server_addr; if(argc<2) { printf("usage :%s tcp_port\n",argv[0]); return -1; } //소켓생성 if((s=socket(PF_INET,SOCK_STREAM,0))<0) { perror("server:can't open stream socket\n"); return -1; } 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 locla address\n"); return -1; } //client로부터 연결기다림 listen(s,5); FD_ZERO(&read_fds);//초기화 nfds=s+1; while(1) { //(s-1)값갱신 if((num-1)>=0) nfds=client_s[num-1]+1; FD_SET(s,&read_fds); //초기소켓선택 for(i=0;i<num;i++) FD_SET(client_s[i],&read_fds); if(select(nfds,&read_fds,0,0,0)<0) { printf("select error\n"); return -1; } if(FD_ISSET(s,&read_fds)) { //초기소켓 s에서 입력발생 clilen=sizeof(client_addr); client_fd=accept(s,(struct sockaddr *)&client_addr,&clilen); if(client_fd !=-1) { client_s[num]=client_fd; num++; send(client_fd,buf,strlen(buf),0); } } //client에 메세지도착검색 //client소켓버호 배열 client[]를 검색 for(i=0;i<num;i++) { if(FD_ISSET(client_s[i],&read_fds)) { if((n=recv(client_s[i],rline,maxline,0))>0) { rline[n]='\0'; //종료문자입력시 탈퇴 if(strncmp(rline,escapechar,5)==1) { shutdown(client_s[i],2); if(i !=num-1) client_s[i]=client_s[num-1]; num--; continue; } //모든 client에 메세지전송 for(j=0;j<num;j++) send(client_s[j],rline,n,0); printf("%s",rline); } } } } }
===========chat_client.c========================
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <fcntl.h> #include <sys/time.h> #define maxline 1024 char *escapechar="exit\n"; int readline(int fd,char *ptr , int maxlen) { int n,r; char c; for(n=1;n<maxlen;n++) { if((r=read(fd,&c,1))==1) { *ptr++=c; if(c=='\n'); break; } else if(r==0) { if(n==1) return(0); else break; } } *ptr=0; return(n); } int main(int argc,char *argv[]) { char line[maxline],sendline[maxline+1]; int s,n,pid,size; int nfds; struct sockaddr_in server_addr; fd_set read_fds; //구조체 if(argc <3) { printf("usage: %s server_address tcp_port\n",argv[0]); return -1; } //소켓생성 if((s=socket(PF_INET,SOCK_STREAM,0))<0) { printf("client:can't open stream socket\n"); return -1; } //소켓주소 구조체 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("clinet: can't connect to server\n"); return -1; } nfds=s+1; FD_ZERO(&read_fds); //초기화 //select호출 while(1) { FD_SET(0,&read_fds); FD_SET(s,&read_fds); if(select(nfds,&read_fds,NULL,NULL,NULL)<0) { printf("select error\n"); return -1; } //서버가 보내오는 메세지출력 if(FD_ISSET(s,&read_fds)) { char recvline[maxline]; 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",sendline); if(send(s,line,size,0) !=(size)) { printf("error:written error..\n"); } if(size==5 && strncmp(sendline,escapechar,5)==0) { close(s); return -1; } } } } }
Forums:
채팅 프로그램이란...
저 생각을 좀 적어 보겠습니다.
채팅 프로그램의 기본은 텍스트 에디터라고 생각됩니다. 왜냐하면 네트웍 기능을 빼면 딱 텍스트 에디터라 생각되거든요. 통상 에디터는 입력기능과 출력기능이 분리되 있으며 별도의 쓰레드로 돌아갑니다. 출력기능은 버퍼를 계속읽으려고 노력하면서 블럭됩니다. 입력기능은 입력을 기다려며 블럭됐다가 입력이 들어오면 버퍼에 기록을 하며 또 입력을 기다리며 블럭됩니다. 출력기능은 버퍼에 내용이 들어오면 그걸 읽어서 출력하며 또 버퍼를 읽으려고 노력하며 기다립니다.
딱 감이 오시죠.?.맞습니다..맞고요..네트웍만 연결하면 바로 채팅 프로그램이 됩니다.
이제 문제는 네트웍인데 사실 핵심기술은 p2p입니다. 서버는 단순히 연결만 해주죠. 실제 통신는 서버를 통해서 하는 것이 아니라 pc간에 알아서 합니다.
이런 방식이 아닐까 생각됩니다. 일단 서버에 접속해서 자신의 아이피를 등록합니다.
그리고 서버에 쿼리를 날려 내 친구들이 등록돼 있는지 알아오며 아이피를 같이 알아옵니다. 서버는 또 그 친구에게 내가 접속했다고 알려줍니다. 여기서 서버는 자기 역활끝입니다. 이젠 클라이어트끼리의 문제죠...
메신저의 버튼을 클릭합니다. 그러면 알아온 아이피주소를 연결을 합니다. 그러면 그쪽편은 접속을 허용하고 입력버퍼를 할당합니다. 그러면서 그쪽 메시저는 이 할당된 버퍼를 계속 체크합니다. 입력이 오면 출력합니다. 그러면서 내 메시저도 같은 동작을 합니다. 연결이 성립니면 입력받을 버퍼를 생성하고 읽기모드로 들갑니다.
위 부분이 올리시 코드와 비슷하지 않을까 하는데 select로 하시기 보단 하나의 소켓을 완전히 새로 생성하는 것이 먼저가 아닐까 생각합니다. 후에 여러명이 동시 채팅을 하게되면 그때 select로의 확장을 고려해보심이...
대충 이렇게 돌아가지 않을까 생각합니다...
p2p의 구체적인기술은...저두 모릅니다..-.-;;
어느정도 비슷한 개념인지 모르겠습니다..쩝..
혹 더 자세히 정확히 알고 계신분은 메일좀.부탁드립니다...^^
하나의 프로세스에서 multiplexing 하는 구조로 작성하셨군요.
하나의 프로세스에서 multiplexing 하는 구조로 작성하셨군요.
좋은 예제입니다.
이 초기화부분은 select가 들어갈때마다 FD_SET 이전에 해주셔야합니다.
while문 안으로 집어 넣으세요.
이 부분은 가정이 client_s[] 중에서 가장 마지막에 들어간 descriptor가 가장 큰 정수 값이라는 가정을 하고 있군요.
그것 보다는 for loop을 돌면서 가장 큰 값을 찾고 그 값에 + 1 하는 것이 의미상 맞는 것 같습니다.
접속되자마자 buf 내용을 전송하는 데요.. buf는 어디서도 초기화되지 않는 것 같군요. 아예 send 부분은
Welcome messagage를 고정해서 전송하는 것이 좋을 것 같습니다.
이 부분중에서 아래 부분이
소켓이 상대에 의해 종료될때, 서버가 종료시킬때, error가 났을때 공통적으로 사용하는 desciptor제거루틴입니다.
이부분을 함수화하시는 것이 좋을 것 같구요. 왜 i != num-1으로 비교하시는지 모르겠습니다만..
이 부분은 for loop을 통하여 남은 부분을 모두 shift해야할 것 같습니다.
소켓이 종료될때 확인을 안해주셨군요. 가능하면 switch 문으로 하시면 좋습니다.
클라이언트 쪽은 언뜻보기에 무리는 없을 것 같습니다.
나중에 기회 되시면, nonblocking I/O도 해보시고, process를 fork 하면서 IPC를 이용하여 전송도 해보시고, thread를 사용해서도 작성해보세요.
---
http://coolengineer.com
댓글 달기