학부 시절 시스템프로그래밍 시간에 소켓 통신에 대한 실습을 진행했었다. 면접 준비를 하며 복습을 하는데 기억이 안 나는 부분이 많아 다시 정리를 해보려고 한다. 리눅스 환경(Mac OS)과 C 언어로 작성을 하였으며 소스 코드는 좋은 코딩님의 블로그를 참조하였다. 링크는 레퍼런스에 달아놓음 ㅎㅎ
Client의 connect() 메서드와 Server의 accept() 메서드로 소켓이 연결된다.
이 때, 3 way handshake 기법을 사용한다.
#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>
#include<time.h>
#define BUF_SIZE 100
#define MAX_CLNT 100
#define MAX_IP 30
void * handle_clnt(void *arg);
void send_msg(char *msg, int len);
void error_handling(char *msg);
char* serverState(int count);
void menu(char port[]);
/****************************/
int clnt_cnt=0;
int clnt_socks[MAX_CLNT];
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;
/** time log **/
struct tm *t;
time_t timer = time(NULL);
t=localtime(&timer);
if (argc != 2)
{
printf(" Usage : %s <port>\n", argv[0]);
exit(1);
}
menu(argv[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)
{
t=localtime(&timer);
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(" Connceted client IP : %s ", inet_ntoa(clnt_adr.sin_addr));
printf("(%d-%d-%d %d:%d)\n", t->tm_year+1900, t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min);
printf(" chatter (%d/100)\n", clnt_cnt);
}
close(serv_sock);
return 0;
}
void *handle_clnt(void *arg)
{
int clnt_sock=*((int*)arg);
int str_len=0, i;
char msg[BUF_SIZE];
while((str_len=read(clnt_sock, msg, sizeof(msg)))!=0)
send_msg(msg, str_len);
// remove disconnected client
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];
break;
}
}
clnt_cnt--;
pthread_mutex_unlock(&mutx);
close(clnt_sock);
return NULL;
}
void send_msg(char* msg, int len)
{
int i;
pthread_mutex_lock(&mutx);
for (i=0; i<clnt_cnt; i++)
write(clnt_socks[i], msg, len);
pthread_mutex_unlock(&mutx);
}
void error_handling(char *msg)
{
fputs(msg, stderr);
fputc('\n', stderr);
exit(1);
}
char* serverState(int count)
{
char* stateMsg = malloc(sizeof(char) * 20);
strcpy(stateMsg ,"None");
if (count < 5)
strcpy(stateMsg, "Good");
else
strcpy(stateMsg, "Bad");
return stateMsg;
}
void menu(char port[])
{
system("clear");
printf(" **** moon/sun chat server ****\n");
printf(" server port : %s\n", port);
printf(" server state : %s\n", serverState(clnt_cnt));
printf(" max connection : %d\n", MAX_CLNT);
printf(" **** Log ****\n\n");
}
/** chat_client **/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<pthread.h>
#include<time.h>
#define BUF_SIZE 100
#define NORMAL_SIZE 20
void* send_msg(void* arg);
void* recv_msg(void* arg);
void error_handling(char* msg);
void menu();
void changeName();
void menuOptions();
char name[NORMAL_SIZE]="[DEFALT]"; // name
char msg_form[NORMAL_SIZE]; // msg form
char serv_time[NORMAL_SIZE]; // server time
char msg[BUF_SIZE]; // msg
char serv_port[NORMAL_SIZE]; // server port number
char clnt_ip[NORMAL_SIZE]; // client ip address
int main(int argc, char *argv[])
{
int sock;
struct sockaddr_in serv_addr;
pthread_t snd_thread, rcv_thread;
void* thread_return;
if (argc!=4)
{
printf(" Usage : %s <ip> <port> <name>\n", argv[0]);
exit(1);
}
/** local time **/
struct tm *t;
time_t timer = time(NULL);
t=localtime(&timer);
sprintf(serv_time, "%d-%d-%d %d:%d", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour,
t->tm_min);
sprintf(name, "[%s]", argv[3]);
sprintf(clnt_ip, "%s", argv[1]);
sprintf(serv_port, "%s", argv[2]);
sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling(" conncet() error");
/** call menu **/
menu();
pthread_create(&snd_thread, NULL, send_msg, (void*)&sock);
pthread_create(&rcv_thread, NULL, recv_msg, (void*)&sock);
pthread_join(snd_thread, &thread_return);
pthread_join(rcv_thread, &thread_return);
close(sock);
return 0;
}
void* send_msg(void* arg)
{
int sock=*((int*)arg);
char name_msg[NORMAL_SIZE+BUF_SIZE];
char myInfo[BUF_SIZE];
char* who = NULL;
char temp[BUF_SIZE];
/** send join messge **/
printf(" >> join the chat !! \n");
sprintf(myInfo, "%s's join. IP_%s\n",name , clnt_ip);
write(sock, myInfo, strlen(myInfo));
while(1)
{
fgets(msg, BUF_SIZE, stdin);
// menu_mode command -> !menu
if (!strcmp(msg, "!menu\n"))
{
menuOptions();
continue;
}
else if (!strcmp(msg, "q\n") || !strcmp(msg, "Q\n"))
{
close(sock);
exit(0);
}
// send message
sprintf(name_msg, "%s %s", name,msg);
write(sock, name_msg, strlen(name_msg));
}
return NULL;
}
void* recv_msg(void* arg)
{
int sock=*((int*)arg);
char name_msg[NORMAL_SIZE+BUF_SIZE];
int str_len;
while(1)
{
str_len=read(sock, name_msg, NORMAL_SIZE+BUF_SIZE-1);
if (str_len==-1)
return (void*)-1;
name_msg[str_len]=0;
fputs(name_msg, stdout);
}
return NULL;
}
void menuOptions()
{
int select;
// print menu
printf("\n\t**** menu mode ****\n");
printf("\t1. change name\n");
printf("\t2. clear/update\n\n");
printf("\tthe other key is cancel");
printf("\n\t*******************");
printf("\n\t>> ");
scanf("%d", &select);
getchar();
switch(select)
{
// change user name
case 1:
changeName();
break;
// console update(time, clear chatting log)
case 2:
menu();
break;
// menu error
default:
printf("\tcancel.");
break;
}
}
/** change user name **/
void changeName()
{
char nameTemp[100];
printf("\n\tInput new name -> ");
scanf("%s", nameTemp);
sprintf(name, "[%s]", nameTemp);
printf("\n\tComplete.\n\n");
}
void menu()
{
system("clear");
printf(" **** moon/sum chatting client ****\n");
printf(" server port : %s \n", serv_port);
printf(" client IP : %s \n", clnt_ip);
printf(" chat name : %s \n", name);
printf(" server time : %s \n", serv_time);
printf(" ************* menu ***************\n");
printf(" if you want to select menu -> !menu\n");
printf(" 1. change name\n");
printf(" 2. clear/update\n");
printf(" **********************************\n");
printf(" Exit -> q & Q\n\n");
}
void error_handling(char* msg)
{
fputs(msg, stderr);
fputc('\n', stderr);
exit(1);
}
https://good-coding.tistory.com/17
https://velog.io/@emplam27/CS-그림으로-알아보는-네트워크-소켓-프로그래밍과-Handshaking
시프라고 함은 혹시 인하인..?ㅎㅎ