Difference between r1.1 and the current
@@ -718,10 +718,15 @@
=== 기능 ===
* 리눅스 멀티 채팅
* 계정 생성 및 비밀번호 생성
=== 버그 ===
* 터미널 세로 크기가 20 이하일 경우 화면이 꼬임
* 메세지 작성 중, 다른 유저가 메세지를 보낼 시 작성 중이던 부분이 보이지 않음.
* 서버 주소가 127.0.0.1(loopback)로 설정되어있음.
----
* 리눅스 멀티 채팅
* 계정 생성 및 비밀번호 생성
* 계정으로 로그인시 부재 중 대화 내역을 볼 수 있음
* 동시접속 최대 20명까지 가능
* 아이디 최대 100개까지 생성 가능
* 간단한 명령어 가능 (명령어 목록 보기, 접속 인원 보기, 귓속말)=== 버그 ===
* 터미널 세로 크기가 20 이하일 경우 화면이 꼬임
* 메세지 작성 중, 다른 유저가 메세지를 보낼 시 작성 중이던 부분이 보이지 않음.
* ID 최대 생성 갯수를 넘을 시 렉이 걸림
* 부재 중 대화 내역이 100개가 넘을시 그 이전 기록은 볼 수 없음.
=== 기타 사항 ===* 서버 주소가 127.0.0.1(loopback)로 설정되어있음.
----
서버 ¶
#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<pthread.h>
#define BUFF_SIZE 1024
#define ACCEPT 0
#define REJECT 1
#define DISCONNECT -1
int thread_num[25]; //스레드 번호 (해당 스레드 활성화시 번호 값 + 1, 비활성화시 0)
//ex) thread_num[스레드 번호]==스레드 번호+1
int thread_id[25]; //스레드 ID 번호
//ex) 스레드 아이디 = id[thread_id[스레드 번호]]
int client_socket_array[25]; //클라이언트 소캣, 각 스레드 마다 자신의 번호에 해당하는 소캣 사용
//ex) 스레드가 사용 중인 소캣 == client_socket_array[스레드 번호]
char id[100][256];
char password[100][256];
char id_state[100];
int id_num;
char chat[100][BUFF_SIZE+5];
char chat_s[100], chat_e;
int stcmp(char* a,char *b)
{
int i;
for(i=0; a[i]&&b[i] ;i++)
{
if(a[i]!=b[i])
return 1;
}
return 0;
}
int send_i(int client_socket,char* arry)//명령어 전송
{
char buff_snd[BUFF_SIZE+5];
sprintf(buff_snd,"i %s\n",arry);
send(client_socket, buff_snd, strlen(buff_snd)+1,0);
}
int send_m(int client_socket,char arry[])//메세지 전송
{
char buff_snd[BUFF_SIZE+5];
sprintf(buff_snd,"m %s\n",arry);
send(client_socket, buff_snd, strlen(buff_snd)+1,0);
}
int login(int t_num)
{
int i;
char buff_rcv[BUFF_SIZE+5];
int client_socket;
client_socket=client_socket_array[t_num-1];
printf("%dth clinet try to login\n",t_num);
while(1)
{
send_i(client_socket,"i");
if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0)
{
return DISCONNECT;
}
for(i=0;i<id_num;i++)
{
if(!stcmp(id[i],buff_rcv))
break;
}
if(i==id_num)
{
send_m(client_socket,"ID is wrong");
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
return DISCONNECT;
if(buff_rcv[0]=='a')
break;
}
continue;
}
else
{
if(id_state[i])
{
send_m(client_socket,"ID is being used.");
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
return DISCONNECT;
if(buff_rcv[0]=='a')
break;
}
continue;
}
}
send_i(client_socket,"p");//password 요구
if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0)
{
return DISCONNECT;
}
if(stcmp(password[i],buff_rcv))
{
send_m(client_socket, "password is wrong");
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
return DISCONNECT;
if(buff_rcv[0]=='a')
break;
}
continue;
}
send_i(client_socket,"a");//접속 확인
return i;
}
}
int new_ID(int t_num)
{
int i;
char buff_rcv[BUFF_SIZE+5];
int client_socket;
client_socket=client_socket_array[t_num-1];
printf("%dth clinet try to create ID\n",t_num);
while(1)
{
send_i(client_socket,"i");
if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0)
{
return DISCONNECT;
}
for(i=0;i<id_num;i++)
{
if(!stcmp(id[i],buff_rcv))
break;
}
if(i==id_num)
{
sprintf(id[id_num],"%s",buff_rcv);
id_num++;
}
else
{
send_m(client_socket,"ID is already existing..");
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
return DISCONNECT;
if(buff_rcv[0]=='a')
break;
}
continue;
}
send_i(client_socket,"p");//password 요구
if(recv(client_socket, buff_rcv,BUFF_SIZE,0)<=0)
{
return DISCONNECT;
}
sprintf(password[i],"%s",buff_rcv);
send_i(client_socket,"a");//접속 확인
return i;
}
}
void command(int t_num, char buff_rcv[])
{
int i,j,k;
char temp_buff[BUFF_SIZE+1];
char buff_snd[BUFF_SIZE+1];
sprintf(temp_buff,"./help\n");
if(!stcmp(temp_buff,buff_rcv))
{
send_m(client_socket_array[t_num-1],"<Command list>");
send_m(client_socket_array[t_num-1],"help : show list of command");
send_m(client_socket_array[t_num-1],"list : show list of online user");
send_m(client_socket_array[t_num-1],"message \"ID\" \"message\" : send message to ID");
send_m(client_socket_array[t_num-1],"\n");
return;
}
sprintf(temp_buff,"./list\n");
if(!stcmp(temp_buff,buff_rcv))
{
send_m(client_socket_array[t_num-1],"<Online user list>");
for(i=0;i<20;i++)
if(thread_num[i] && thread_id[i]>=0)
{
send_m(client_socket_array[t_num-1],id[thread_id[i]]);
}
send_m(client_socket_array[t_num-1],"\n");
return;
}
sprintf(temp_buff,"./message ");
for(i=0;i<10;i++)
{
if(temp_buff[i]!=buff_rcv[i])
break;
}
if(i==10)
{
for(i=0;i<id_num;i++)
{
for(j=0;buff_rcv[j+10]!=' ';j++)
{
if(id[i][j]!=buff_rcv[j+10])
break;
}
if(buff_rcv[j+10]==' ')
{
if(id_state[i])
{
sprintf(temp_buff,"send to %s : %s", id[i], &buff_rcv[j+11] );
send_m(client_socket_array[t_num-1],temp_buff);
for(k=0;k<20;k++)
if(thread_id[k]==i)
{
sprintf(temp_buff,"message from %s : %s",
id[thread_num[t_num-1]],&buff_rcv[j+11]);
send_m(client_socket_array[k],temp_buff);
break;
}
break;
}
else
{
send_m(client_socket_array[t_num-1],"ID is offline");
break;
}
}
}
if(i==id_num)
send_m(client_socket_array[t_num-1],"ID is wrong");
}
}
void message(int t_num, char buff_rcv[])
{
int i;
char buff_snd[BUFF_SIZE+1];
sprintf(buff_snd,"%s : %s",id[thread_id[t_num-1]],buff_rcv);
for(i=0;i<20;i++)
{
if(thread_num[i] && thread_id[i]>=0)
send_m(client_socket_array[i],buff_snd);
}
sprintf(chat[chat_e],"%s",buff_snd);
chat_e++;
chat_e%=100;
for(i=0;i<100;i++)
if(id_state[i] || i>=id_num)
chat_s[i]=chat_e;
}
void disconnect(int t_num, int i_num)
{
printf("%dth client is disconnected\n",t_num);
id_state[i_num]=0;
thread_num[t_num-1]=0;
close(client_socket_array[t_num-1]);
client_socket_array[t_num-1]=0;
thread_id[t_num-1]=0;
}
void* rutine(void* data)//data = &thread_num[스레드 번호]
{
int t_num,i_num;//스레드 번호, 아이디 번호
int client_socket;
char buff_rcv[BUFF_SIZE+5],buff_snd[BUFF_SIZE+5];
int rcv;
int i,j;
t_num=*((int*)data);
//사용자가 이해하기 쉽도록 스레드 번호에 +1 값을 쓰도록 한다.
client_socket=client_socket_array[t_num-1];
//코드의 간결화를 위해 값을 복사한다.
printf("%dth client is connected, socket_num is %d\n",t_num, client_socket);
thread_id[t_num-1]=-1;
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
{
disconnect(t_num,i_num);
return;
}
if(buff_rcv[0] == 'l')
{
send_i(client_socket,"a");
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
{
disconnect(t_num,i_num);
return;
}
else
{
if(buff_rcv[0]=='a')
break;
}
}
i_num=login(t_num);
break;
}
else
{
if(buff_rcv[0] == 'n')
{
send_i(client_socket,"a");
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
{
disconnect(t_num,i_num);
return;
}
else
{
if(buff_rcv[0]=='a')
break;
}
}
i_num=new_ID(t_num);
break;
}
else
{
send_i(client_socket,"c");
}
}
}
if(i_num==DISCONNECT)
{
disconnect(t_num,i_num);
return;
}
thread_id[t_num-1]=i_num;
id_state[i_num]=1;
//로그인 도중 메세지가 전송되는 것을 방지, 클라이언트가 작업을 완료하는 것을 기다린다.
while(recv(client_socket,buff_rcv,BUFF_SIZE,0))
{
if(buff_rcv[0]=='a')
break;
}
sprintf(buff_snd,"log\n");
send_m(client_socket,buff_snd);
for(;chat_s[i_num]!=chat_e;chat_s[i_num]++)
{
sprintf(buff_snd,"%s\n",chat[chat_s[i_num]]);
send_m(client_socket, buff_snd);
}
sprintf(buff_snd,"%s has joined",id[i_num]);
for(i=0;i<20;i++)
{
if(thread_num[i] && thread_id[i]>=0)
send_m(client_socket_array[i],buff_snd);
}
while(1)
{
rcv=recv(client_socket,buff_rcv,BUFF_SIZE,0);
if(rcv>0)
{
printf("received\n");
if(buff_rcv[0]=='.' && buff_rcv[1]=='/')
command(t_num,buff_rcv);
else
message(t_num,buff_rcv);
}
else
{
break;
}
}
sprintf(buff_snd,"%s has left",id[i_num]);
for(i=0;i<20;i++)
{
if(thread_num[i])
send_m(client_socket_array[i],buff_snd);
}
disconnect(t_num, i_num);
}
int main()
{
int server_socket;
int client_socket;
pthread_t p_thread[25];
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int client_addr_size;
int i;
server_socket=socket(PF_INET, SOCK_STREAM, 0);
if(server_socket==-1)
{
printf("socket error\n");
exit(1);
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family =AF_INET;
server_addr.sin_port =htons(4000);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
if(-1==bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
{
printf("bind error\n");
exit(1);
}
printf("server started\n");
for(i=0;i<20;i++)
thread_num[i]=0;
while(1)
{
if(-1==listen(server_socket,5))
{
printf("listen error\n");
exit(1);
}
client_socket = accept(server_socket, (struct sockaddr*)&client_addr,&client_addr_size);
if(client_socket==-1)
continue;
for(i=0;i<20;i++)
{
if(!thread_num[i])
{
client_addr_size= sizeof(client_addr);
client_socket_array[i] = client_socket;
thread_num[i]=i+1;
memset(&p_thread[i],0,sizeof(p_thread[i]));
pthread_create(&p_thread[i],NULL,rutine,(void *)&thread_num[i]);
break;
}
}
if(i==20)
printf("error : Too many clients connected\n");
}
close(server_socket);
return 0;
}
클라이언트 ¶
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
#define BUFF_SIZE 1024
void gotoxy(int x,int y)
{
printf("\033[%d;%df",y,x);
//터미널 출력 위치 변경 함수, y값이 화면 길이보다 크면 화면 길이로 처리된다.(버그의 원인)
fflush(stdout);
}
int client_socket;
int check;
void* rcv_thread(void *data)
{
int i;
char buff_rcv[BUFF_SIZE+5];
int rcv;
while(1)
{
rcv=recv(client_socket, buff_rcv, BUFF_SIZE, 0);
if(rcv>0)
{
while(check==1);
check=2;
for(i=0;i<rcv;)
{
if(buff_rcv[i]=='m')
{
i+=2;
gotoxy(0,100);//gotoxy의 특징을 이용하여 화면을 한칸 올린다.
printf("\n");
gotoxy(0,14);
printf(" ");
gotoxy(0,14);
printf("%s",&buff_rcv[i]);
}
if(buff_rcv[i]=='i')
{
}
i+=strlen(&buff_rcv[i])+1;
}
}
else
{
printf("disconnected\n");
break;
}
gotoxy(0,15);
printf("write message to send : ");
gotoxy(25,15);
fflush(stdout);
check=0;
}
}
void* snd_thread(void *data)
{
char buff_snd[BUFF_SIZE+5];
fgets(buff_snd,BUFF_SIZE,stdin);//버퍼 청소
while(1)
{
while(check==2);
check=1;
gotoxy(0,15);
printf("write message to send : ");
gotoxy(25,15);
check=0;
fgets(buff_snd,BUFF_SIZE,stdin);
check=0;
if(send(client_socket, buff_snd, strlen(buff_snd)+1,0)<=0)
{
printf("disconnected\n");
break;
}
}
}
int login(int client_socket)
{
char buff_rcv[BUFF_SIZE+5];
char buff_snd[BUFF_SIZE+5];
//로그인 시스템
while(1)
{
printf("new id=n / login=l : ");
fflush(stdout);
scanf("%s",buff_snd);
send(client_socket,buff_snd,strlen(buff_snd)+1,0);
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)<=0)
{
printf("disconnected\n");
close(client_socket);
return -1;
}
if(buff_rcv[0]=='i')
break;
if(buff_rcv[0]=='m')
printf("%s",&buff_rcv[2]);
}
if(buff_rcv[2]=='a')
break;
}
sprintf(buff_snd,"a");
send(client_socket,buff_snd,strlen(buff_snd)+1,0);
while(1)
{
if(recv(client_socket,buff_rcv,BUFF_SIZE,0)>0)
{
if(buff_rcv[0]=='i')
{
switch(buff_rcv[2])
{
case 'i' :
printf("ID : ");
fflush(stdout);
scanf("%s",buff_snd);
send(client_socket,buff_snd,strlen(buff_snd)+1,0);
break;
case 'p' :
printf("PASSWORD : ");
fflush(stdout);
scanf("%s",buff_snd);
send(client_socket,buff_snd,strlen(buff_snd)+1,0);
break;
}
if( buff_rcv[2] == 'a' )
break;
}
if(buff_rcv[0]=='m')
{
printf("%s",&buff_rcv[2]);
sprintf(buff_snd,"a");
send(client_socket, buff_snd, strlen(buff_snd)+1, 0);
}
}
else
{
printf("disconnected\n");
close(client_socket);
return -1;
}
}
return 0;
}
int main()
{
struct sockaddr_in server_addr;
char buff_snd[BUFF_SIZE+5];
char buff_rcv[BUFF_SIZE+5];
int rcv;
int i;
pthread_t p_thread[2];
client_socket=socket(PF_INET, SOCK_STREAM, 0);
if(client_socket==-1)
{
printf("socket error\n");
return 1;
}
memset( &server_addr, 0, sizeof( server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons( 4000);
server_addr.sin_addr.s_addr= inet_addr( "127.0.0.1");
if(-1==connect(client_socket,(struct sockaddr*)&server_addr, sizeof( server_addr) ) )
{
printf("connect error\n");
return 1;
}
if(login(client_socket))
return 0;
sprintf(buff_snd,"a");
send(client_socket,buff_snd,strlen(buff_snd)+1,0);//메세지 전송을 허용함.
memset(&p_thread[0],0,sizeof(p_thread[0]));
pthread_create(&p_thread[0],NULL,rcv_thread,(void *)NULL);
memset(&p_thread[1],0,sizeof(p_thread[1]));
pthread_create(&p_thread[1],NULL,snd_thread,(void *)NULL);
pthread_join(p_thread[0],(void**)&rcv);
pthread_join(p_thread[1],(void**)&rcv);
close(client_socket);
return 0;
}
설명 ¶
기능 ¶
- 리눅스 멀티 채팅
- 계정 생성 및 비밀번호 생성
- 계정으로 로그인시 부재 중 대화 내역을 볼 수 있음
- 동시접속 최대 20명까지 가능
- 아이디 최대 100개까지 생성 가능
- 간단한 명령어 가능 (명령어 목록 보기, 접속 인원 보기, 귓속말)
버그 ¶
- 터미널 세로 크기가 20 이하일 경우 화면이 꼬임
- 메세지 작성 중, 다른 유저가 메세지를 보낼 시 작성 중이던 부분이 보이지 않음.
- ID 최대 생성 갯수를 넘을 시 렉이 걸림
- 부재 중 대화 내역이 100개가 넘을시 그 이전 기록은 볼 수 없음.










