- 프로젝트 기간 : 2017.05 - 2018.07
- 사용 언어 및 프레임워크 : c
- 프로젝트 github 링크
- 프로젝트 소개 : 1:1 통신에서부터 1:n 통신에 이르기까지 가위바위보, 빙고게임, 채팅프로그램 순으로 프로젝트의 범위를 넓혀가며 실습함
실습 교재
윤성우의 열혈 TCP/IP 프로그래밍
소켓 프로그래밍을 이용한 클라이언트/서버 기반의 학점 계산 프로그램 github
소켓 프로그래밍을 이용한 클라이언트/서버간의 1:1 빙고게임 github
소켓 프로그래밍을 이용한 클라이언트/클라이언트간의 1:1 빙고게임 github
멀티스레드 서버를 이용한 1개의서버/n개의 클라이언트 간의 채팅 프로그램 github
Source Code Example (채팅 프로그램의 chat_server.c)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#define BUF_SIZE 200
#define MAX_CLNT 256
void * handle_clnt(void * arg);
void send_msg(char * msg, int len,int clnt_sock);
void error_handling(char * msg);
int clnt_cnt=0;
int clnt_socks[MAX_CLNT];
char clnt_info[200] = { 0, };
pthread_mutex_t mutx;
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
struct sockaddr_in serv_adr, clnt_adr;
int clnt_adr_sz;
pthread_t t_id;
if(argc!=2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
pthread_mutex_init(&mutx, NULL);
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
while(1)
{
clnt_adr_sz=sizeof(clnt_adr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr,&clnt_adr_sz);
pthread_mutex_lock(&mutx);
clnt_socks[clnt_cnt++]=clnt_sock;
pthread_mutex_unlock(&mutx);
pthread_create(&t_id, NULL, handle_clnt, (void*)&clnt_sock);
pthread_detach(t_id);
printf("Connected client IP: %s \n", inet_ntoa(clnt_adr.sin_addr));
}
close(serv_sock);
return 0;
}
/*생성된 스레드가 처리하는 함수*/
void * handle_clnt(void * arg)
{
int clnt_sock = *((int*)arg);
int str_len = 0, i, k = 0;
char msg[BUF_SIZE] = { 0, };
char newclnt[30];
char clnt_list[100];
char clnt_close[100];
/*새로운 클라이언트 접속 시에 다른 클라이언트들에게 접속 정보 제공*/
pthread_mutex_lock(&mutx);
read(clnt_sock, msg, sizeof(msg));
sprintf(newclnt, "New client %s!", msg);
for (i = 0; i < clnt_cnt; i++){
if (clnt_sock == clnt_socks[i])
continue;
else
write(clnt_socks[i], newclnt, sizeof(newclnt));
}
/*새로운 클라이언트 접속 시에 해당 클라이언트에게 대화방 정보 제공*/
strcat(clnt_info, msg);
sprintf(clnt_list, "현재 접속 중인 클라이언트(총%d명) : %s", clnt_cnt, clnt_info);
write(clnt_sock, clnt_list, sizeof(clnt_list));
pthread_mutex_unlock(&mutx);
while ((str_len = read(clnt_sock, msg, sizeof(msg))) != 0){
/*클라이언트 접속 종료 시 다른 클라이언트들에게 접속 종료 정보 제공*/
if ((msg[0] == 'Q') || (msg[0] == 'q')){
sprintf(clnt_close, "%s 님이 접속을 종료하였습니다.\n", &msg[1]);
pthread_mutex_lock(&mutx);
for (i = 0; i < clnt_cnt; i++){
write(clnt_socks[i], clnt_close, sizeof(clnt_close));
}
pthread_mutex_unlock(&mutx);
}
else
send_msg(msg, str_len, clnt_sock);
}
/*클라이언트 접속 종료시 전역변수 처리 과정*/
pthread_mutex_lock(&mutx);
for(i=0; i<clnt_cnt; i++)
{
if (clnt_sock == clnt_socks[i])
{
while (i < clnt_cnt - 1){
clnt_socks[i] = clnt_socks[i + 1]; i++;
}
break;
}
}
clnt_cnt--;
pthread_mutex_unlock(&mutx);
close(clnt_sock);
return NULL;
}
/*클라이언트에게 메세지 전송*/
void send_msg(char * msg, int len,int clnt_sock)
{
int i;
/*입력한 메세지는 본인을 제외한 클라이언트들에게 전송되도록 설정*/
pthread_mutex_lock(&mutx);
for (i = 0; i < clnt_cnt; i++){
if (clnt_sock == clnt_socks[i])
continue;
else
write(clnt_socks[i], msg, len);
}
pthread_mutex_unlock(&mutx);
}
void error_handling(char * msg)
{
fputs(msg, stderr);
fputc('\n', stderr);
exit(1);
}