본문 바로가기
Ubuntu

Ubuntu TCP/IP 단체 채팅 프로그램

by TYB 2024. 2. 20.
반응형

 

기본은 전체 전송하는 메시지

그냥 string을 쳐주고 enter를 치면 전송됨.

 

패킷은 사이즈에 따라 돈을 내야함. 불필요한 패킷은 없애야함. 프로세스라고 보면 할 일 없을 때 sleep을 해야되는데, 자신의 메시지도 아닌데 interrupt로 깨어나서 처리해야되니, 괜히 깨어난거임. 고로 1대1 메시지 기능이 있음.

[13] ㅇㅇ 이런식으로 보내면 13번에게만 메시지를 보내줌.

 

[idlist]를 치면 접속되어 있는 클라이언트의 리스트가 쭉 나옴.

 

여러 client와의 통신연결이 서로 다른 스레드에서 지속적으로 이어져 있고 각각의 동작을 하기 때문에, 전역변수, 전역함수를 mutex로 막아줘야함.

 

 

ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ ㅇ

 

 

 

 

iot_client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <signal.h>

#define BUF_SIZE 100
#define NAME_SIZE 20
#define ARR_CNT 5

void * send_msg(void * arg);
void * recv_msg(void * arg);
void error_handling(char * msg);

char name[NAME_SIZE]="[Default]";
char msg[BUF_SIZE];

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);
	}

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

	sock = socket(PF_INET, SOCK_STREAM, 0);
	if(sock == -1)
		error_handling("socket() error");

	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("connect() error");

	sprintf(msg,"[%s:PASSWD]",name);
	write(sock, msg, strlen(msg));
	pthread_create(&rcv_thread, NULL, recv_msg, (void *)&sock);
	pthread_create(&snd_thread, NULL, send_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;
	int str_len;
	int ret;
	fd_set initset, newset;
	struct timeval tv;
	char name_msg[NAME_SIZE + BUF_SIZE+2];

	FD_ZERO(&initset);
	FD_SET(STDIN_FILENO, &initset);

	fputs("Input a message! [ID]msg (Default ID:ALLMSG)\n",stdout);
	while(1) {
		memset(msg,0,sizeof(msg));
		name_msg[0] = '\0';
		tv.tv_sec = 1;
		tv.tv_usec = 0;
		newset = initset;
		ret = select(STDIN_FILENO + 1, &newset, NULL, NULL, &tv);
		if(FD_ISSET(STDIN_FILENO, &newset))
		{
			fgets(msg, BUF_SIZE, stdin);
			if(!strncmp(msg,"quit\n",5)) {
				*sock = -1;
				return NULL;
			}
			else if(msg[0] != '[')
			{
				strcat(name_msg,"[ALLMSG]");
				strcat(name_msg,msg);
			}
			else
				strcpy(name_msg,msg);
			if(write(*sock, name_msg, strlen(name_msg))<=0)
			{
				*sock = -1;
				return NULL;
			}
		}
		if(ret == 0) 
		{
			if(*sock == -1) 
				return NULL;
		}
	}
}

void * recv_msg(void * arg)
{
	int * sock = (int *)arg;	
	int i;
	char *pToken;
	char *pArray[ARR_CNT]={0};

	char name_msg[NAME_SIZE + BUF_SIZE +1];
	int str_len;
	while(1) {
		memset(name_msg,0x0,sizeof(name_msg));
		str_len = read(*sock, name_msg, NAME_SIZE + BUF_SIZE );
		if(str_len <= 0) 
		{
			*sock = -1;
			return NULL;
		}
		name_msg[str_len] = 0;
		fputs(name_msg, stdout);

		/*   	pToken = strtok(name_msg,"[:]");
			i = 0;
			while(pToken != NULL)
			{
			pArray[i] =  pToken;
			if(i++ >= ARR_CNT)
			break;
			pToken = strtok(NULL,"[:]");
			}

		//		printf("id:%s, msg:%s,%s,%s,%s\n",pArray[0],pArray[1],pArray[2],pArray[3],pArray[4]);
		printf("id:%s, msg:%s\n",pArray[0],pArray[1]);
		*/
	}
}

void error_handling(char * msg)
{
	fputs(msg, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

 

iot_server.c

/* author : KSH */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>

#define BUF_SIZE 100
#define MAX_CLNT 32
#define ID_SIZE 10
#define ARR_CNT 5

#define DEBUG
typedef struct {
		char fd;
		char *from;
		char *to;
		char *msg;
		int len;
}MSG_INFO;

typedef struct {
		int index;
		int fd;
		char ip[20];
		char id[ID_SIZE];
		char pw[ID_SIZE];
}CLIENT_INFO;

void * clnt_connection(void * arg);
void send_msg(MSG_INFO * msg_info, CLIENT_INFO * first_client_info);
void error_handling(char * msg);
void log_file(char * msgstr);

int clnt_cnt=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;
		int sock_option  = 1;
		pthread_t t_id[MAX_CLNT] = {0};
		int str_len = 0;
		int i;
		char idpasswd[(ID_SIZE*2)+3];
		char *pToken;
		char *pArray[ARR_CNT]={0};
		char msg[BUF_SIZE];

		CLIENT_INFO client_info[MAX_CLNT] = {{0,-1,"","1","PASSWD"}, \
				{0,-1,"","2","PASSWD"},  {0,-1,"","3","PASSWD"}, \
				{0,-1,"","4","PASSWD"},  {0,-1,"","5","PASSWD"}, \
				{0,-1,"","6","PASSWD"},  {0,-1,"","7","PASSWD"}, \
				{0,-1,"","8","PASSWD"},  {0,-1,"","9","PASSWD"}, \
				{0,-1,"","10","PASSWD"},  {0,-1,"","11","PASSWD"}, \
				{0,-1,"","12","PASSWD"},  {0,-1,"","13","PASSWD"}, \
				{0,-1,"","14","PASSWD"},  {0,-1,"","15","PASSWD"}, \
				{0,-1,"","16","PASSWD"},  {0,-1,"","17","PASSWD"}, \
				{0,-1,"","18","PASSWD"},  {0,-1,"","19","PASSWD"}, \
				{0,-1,"","20","PASSWD"},  {0,-1,"","21","PASSWD"}, \
				{0,-1,"","22","PASSWD"},  {0,-1,"","23","PASSWD"}, \
				{0,-1,"","24","PASSWD"},  {0,-1,"","25","PASSWD"}, \
				{0,-1,"","26","PASSWD"},  {0,-1,"","27","PASSWD"}, \
				{0,-1,"","28","PASSWD"},  {0,-1,"","29","PASSWD"}, \
				{0,-1,"","30","PASSWD"},  {0,-1,"","31","PASSWD"}, \
				{0,-1,"","HM_CON","PASSWD"}};

		if(argc != 2) {
				printf("Usage : %s <port>\n",argv[0]);
				exit(1);
		}
		fputs("IoT Server Start!!\n",stdout);

		if(pthread_mutex_init(&mutx, NULL))
				error_handling("mutex init error");

		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]));

		setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&sock_option, sizeof(sock_option));
		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);
				if(clnt_cnt >= MAX_CLNT)
				{
						printf("socket full\n");
						shutdown(clnt_sock,SHUT_WR);
						continue;
				}
				else if(clnt_sock < 0)
				{
						perror("accept()");
						continue;
				}

				str_len = read(clnt_sock, idpasswd, sizeof(idpasswd));
				idpasswd[str_len] = '\0';

				if(str_len > 0)
				{
						i=0;
						pToken = strtok(idpasswd,"[:]");

						while(pToken != NULL)
						{
								pArray[i] =  pToken;
								if(i++ >= ARR_CNT)
										break;	
								pToken = strtok(NULL,"[:]");
						}
						for(i=0;i<MAX_CLNT;i++)
						{
								if(!strcmp(client_info[i].id,pArray[0]))
								{
										if(client_info[i].fd != -1)
										{
												sprintf(msg,"[%s] Already logged!\n",pArray[0]);
												write(clnt_sock, msg,strlen(msg));
												log_file(msg);
												shutdown(clnt_sock,SHUT_WR);
#if 1   //for MCU
												client_info[i].fd = -1;
#endif  
												break;
										}
										if(!strcmp(client_info[i].pw,pArray[1])) 
										{

												strcpy(client_info[i].ip,inet_ntoa(clnt_adr.sin_addr));
												pthread_mutex_lock(&mutx);
												client_info[i].index = i; 
												client_info[i].fd = clnt_sock; 
												clnt_cnt++;
												pthread_mutex_unlock(&mutx);
												sprintf(msg,"[%s] New connected! (ip:%s,fd:%d,sockcnt:%d)\n",pArray[0],inet_ntoa(clnt_adr.sin_addr),clnt_sock,clnt_cnt);
												log_file(msg);
												write(clnt_sock, msg,strlen(msg));

												pthread_create(t_id+i, NULL, clnt_connection, (void *)(client_info + i));
												pthread_detach(t_id[i]);
												break;
										}
								}
						}
						if(i == MAX_CLNT)
						{
								sprintf(msg,"[%s] Authentication Error!\n",pArray[0]);
								write(clnt_sock, msg,strlen(msg));
								log_file(msg);
								shutdown(clnt_sock,SHUT_WR);
						}
				}
				else 
						shutdown(clnt_sock,SHUT_WR);

		}
		return 0;
}

void * clnt_connection(void *arg)
{
		CLIENT_INFO * client_info = (CLIENT_INFO *)arg;
		int str_len = 0;
		int index = client_info->index;
		char msg[BUF_SIZE];
		char to_msg[MAX_CLNT*ID_SIZE+1];
		int i=0;
		char *pToken;
		char *pArray[ARR_CNT]={0};
		char strBuff[130]={0};

		MSG_INFO msg_info;
		CLIENT_INFO  * first_client_info;

		first_client_info = (CLIENT_INFO *)((void *)client_info - (void *)( sizeof(CLIENT_INFO) * index ));
		while(1)
		{
				memset(msg,0x0,sizeof(msg));
				str_len = read(client_info->fd, msg, sizeof(msg)-1); 
				if(str_len <= 0)
						break;

				msg[str_len] = '\0';
				pToken = strtok(msg,"[:]");
				i = 0; 
				while(pToken != NULL)
				{
						pArray[i] =  pToken;
						if(i++ >= ARR_CNT)
								break;	
						pToken = strtok(NULL,"[:]");
				}

				msg_info.fd = client_info->fd;
				msg_info.from = client_info->id;
				msg_info.to = pArray[0];
				sprintf(to_msg,"[%s]%s",msg_info.from,pArray[1]);
				msg_info.msg = to_msg;
				msg_info.len = strlen(to_msg);

				sprintf(strBuff,"msg : [%s->%s] %s",msg_info.from,msg_info.to,pArray[1]);
				log_file(strBuff);
				send_msg(&msg_info, first_client_info);
		}

		close(client_info->fd);

		sprintf(strBuff,"Disconnect ID:%s (ip:%s,fd:%d,sockcnt:%d)\n",client_info->id,client_info->ip,client_info->fd,clnt_cnt-1);
		log_file(strBuff);

		pthread_mutex_lock(&mutx);
		clnt_cnt--;
		client_info->fd = -1;
		pthread_mutex_unlock(&mutx);

		return 0;
}

void send_msg(MSG_INFO * msg_info, CLIENT_INFO * first_client_info)
{
		int i=0;

		if(!strcmp(msg_info->to,"ALLMSG"))
		{
				for(i=0;i<MAX_CLNT;i++)
						if((first_client_info+i)->fd != -1)	
								write((first_client_info+i)->fd, msg_info->msg, msg_info->len);
		}
		else if(!strcmp(msg_info->to,"IDLIST"))
		{
				char* idlist = (char *)malloc(ID_SIZE * MAX_CLNT);
				msg_info->msg[strlen(msg_info->msg) - 1] = '\0';
				strcpy(idlist,msg_info->msg);

				for(i=0;i<MAX_CLNT;i++)
				{
						if((first_client_info+i)->fd != -1)	
						{
								strcat(idlist,(first_client_info+i)->id);
								strcat(idlist," ");
						}
				}
				strcat(idlist,"\n");
				write(msg_info->fd, idlist, strlen(idlist));
				free(idlist);
		}
		else
				for(i=0;i<MAX_CLNT;i++)
						if((first_client_info+i)->fd != -1)	
								if(!strcmp(msg_info->to,(first_client_info+i)->id))
										write((first_client_info+i)->fd, msg_info->msg, msg_info->len);
}

void error_handling(char *msg)
{
		fputs(msg, stderr);
		fputc('\n', stderr);
		exit(1);
}

void log_file(char * msgstr)
{
		fputs(msgstr,stdout);
}

 

Makefile

#CC:=arm-linux-gcc
CC:=gcc

TARGET_SRV=iot_server
OBJECT_SRV=$(TARGET_SRV).o 

TARGET_CLN=iot_client
OBJECT_CLN=$(TARGET_CLN).o 

#LDFLAGS=-D_REENTRANT -pthread -lmysqlclient
LDFLAGS=-D_REENTRANT -pthread

all : $(TARGET_SRV) $(TARGET_CLN)

$(TARGET_SRV):$(OBJECT_SRV)
	$(CC) -o $@ $(OBJECT_SRV) $(LDFLAGS)
$(TARGET_CLN):$(OBJECT_CLN)
	$(CC) -o $@ $(OBJECT_CLN) $(LDFLAGS)
%.o:%.c
	$(CC) -c -o $@ $<
clean:
	rm -f *.o $(TARGET_SRV) $(TARGET_CLN)

 

 

같은 폴더에 넣고 make를 치면 자동으로 object file을 만들어줌.

 

 

 

 

반응형

 

반응형