Putty를 활용한 서버 내 채팅 기능 구현
2024. 7. 25. 15:17ㆍ---포트폴리오---/텀 프로젝트
728x90
- 동작원리
(1) 클라이언트가 서버에 접속하면 서버에서 사용할 고유 ID 입력.
(2) 각 클라이언트가 서버에 접속하면 서버 화면에서 클라이언트 접속 알림.
(3) 각 클라이언트가 채팅을 입력하면 상대 클라이언트에게 채팅 입력시간과 상대 클라이언트 고유 ID 및 채팅 출력.
- 클라이언트 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> //유닉스 표준 함수 라이브러리
#include <pthread.h> //POSIX 스레드 라이브러리
#include <arpa/inet.h> //인터넷 프로토콜 주소 라이브러리
#include <time.h> //시간 관련 함수 라이브러리
#define MAX_MESSAGE_LENGTH 1024 //메시지 최대 길이 정의
void *handle_server_message(void *arg); //서버로부터 메시지를 처리하는 스레드 함수 선언
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "사용법: %s <서버 IP> <포트 번호>\n", argv[0]);
exit(EXIT_FAILURE);
}
char *server_ip = argv[1]; //서버 IP 주소
int port = atoi(argv[2]); //포트 번호를 정수로 변환
//클라이언트 소켓 생성
int client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == -1) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
//서버 주소 구조체 설정
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr(server_ip);
server_address.sin_port = htons(port);
//서버 연결
if (connect(client_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
perror("서버 연결 실패");
exit(EXIT_FAILURE);
}
//사용자 이름 입력
printf("사용자 이름을 입력하세요: ");
char client_id[20];
fgets(client_id, sizeof(client_id), stdin);
client_id[strcspn(client_id, "\n")] = '\0';
//서버로 사용자 이름 전송
send(client_socket, client_id, strlen(client_id), 0);
//서버로부터 사용자 이름 수신
char received_id[20];
int bytes_received = recv(client_socket, received_id, sizeof(received_id), 0);
if (bytes_received <= 0) {
perror("서버로부터 사용자 이름 받기 실패");
close(client_socket);
exit(EXIT_FAILURE);
}
received_id[bytes_received] = '\0';
printf("서버에 연결되었습니다. 대화를 시작하세요.\n");
//서버 메시지 처리 스레드 생성
pthread_t server_thread;
if (pthread_create(&server_thread, NULL, handle_server_message, (void *)&client_socket) != 0) {
perror("스레드 생성 실패");
close(client_socket);
exit(EXIT_FAILURE);
}
//사용자가 메시지를 입력하고 전송하는 루프
while (1) {
char message[MAX_MESSAGE_LENGTH];
printf("%s: ", client_id);
fgets(message, sizeof(message), stdin);
if (strcmp(message, "Q\n") == 0 || strcmp(message, "quit\n") == 0) {
break;
}
//현재 시간 가져오기
time_t raw_time;
struct tm *time_info;
time(&raw_time);
time_info = localtime(&raw_time);
char formatted_time[20];
strftime(formatted_time, sizeof(formatted_time), "[%H:%M:%S]", time_info);
char formatted_message[MAX_MESSAGE_LENGTH + 30];
sprintf(formatted_message, "%s %s: %s", formatted_time, clients, message);
//서버로 메시지 전송
send(client[i].client_socket, formatted_message, strlen(formatted_message), 0);
}
pthread_join(server_thread, NULL);
close(client_socket);
return 0;
}
//서버로부터 메시지를 수신하여 출력하는 함수
void *handle_server_message(void *arg) {
int client_socket = *((int *)arg);
char message[MAX_MESSAGE_LENGTH];
while (1) {
//서버로부터 메시지 수신
int bytes_received = recv(client_socket, message, sizeof(message), 0);
if (bytes_received <= 0) {
break;
}
message[bytes_received] = '\0';
//수신한 메시지 출력
printf("%s", message);
}
pthread_exit(NULL);
}
- 서버 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <time.h>
#define MAX_MESSAGE_LENGTH 1024
#define MAX_CLIENTS 2 //최대 클라이언트 수 정의
//클라이언트 정보를 담는 구조체
typedef struct {
int client_socket; //클라이언트 소켓 파일 디스크립터
char client_id[20]; //클라이언트 ID
} Client;
Client clients[MAX_CLIENTS]; //클라이언트 배열
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //뮤택스 초기화
void *handle_client(void *arg); //클라이언트 메시지를 처리하는 함수의 선언
void broadcast(Client *clients, char *message, int sender_socket);
//서버 소켓 생성
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "사용법: %s <포트 번호>\n", argv[0]);
exit(EXIT_FAILURE);
}
int port = atoi(argv[1]);
//서버 주소 구조체 설정
int server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("소켓 생성 실패");
exit(EXIT_FAILURE);
}
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(port);
if (bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
perror("바인드 실패");
close(server_socket);
exit(EXIT_FAILURE);
}
//클라이언트 연결 대기
if (listen(server_socket, 5) == -1) {
perror("리스닝 실패");
close(server_socket);
exit(EXIT_FAILURE);
}
printf("서버가 클라이언트를 대기 중입니다...\n");
while (1) {
//클라이언트 연결 수락
struct sockaddr_in client_address;
socklen_t client_address_len = sizeof(client_address);
int client_socket = accept(server_socket, (struct sockaddr *)&client_address, &client_address_len);
if (client_socket == -1) {
perror("클라이언트 연결 수락 실패");
continue;
}
//클라이언트 ID 수신
char client_id[20];
int bytes_received = recv(client_socket, client_id, sizeof(client_id), 0);
if (bytes_received <= 0) {
perror("사용자 이름 받기 실패");
close(client_socket);
continue;
}
client_id[bytes_received] = '\0';
//클라이언트 ID 전송
send(client_socket, client_id, strlen(client_id), 0);
printf("클라이언트 %s가 연결되었습니다.\n", client_id);
//클라이언트 정보를 배열에 저장
pthread_mutex_lock(&mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i].client_socket == 0) {
clients[i].client_socket = client_socket;
strcpy(clients[i].client_id, client_id);
break;
}
}
pthread_mutex_unlock(&mutex);
pthread_t client_thread;
if (pthread_create(&client_thread, NULL, handle_client, (void *)&client_socket) != 0) {
perror("스레드 생성 실패");
close(client_socket);
continue;
}
pthread_detach(client_thread);
}
close(server_socket);
return 0;
}
void *handle_client(void *arg) {
int client_socket = *((int *)arg);
while (1) {
char message[MAX_MESSAGE_LENGTH];
int bytes_received = recv(client_socket, message, sizeof(message), 0);
if (bytes_received <= 0) {
pthread_mutex_lock(&mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i].client_socket == client_socket) {
printf("클라이언트 %s가 연결을 종료했습니다.\n", clients[i].client_id);
clients[i].client_socket = 0;
break;
}
}
pthread_mutex_unlock(&mutex);
break;
}
message[bytes_received] = '\0';
//메시지를 다른 클라이언트에 브로드캐스트
broadcast(clients, message, client_socket);
}
close(client_socket);
pthread_exit(NULL);
}
//메시지를 브로드캐스트하는 함수
void broadcast(Client *clients, char *message, int sender_socket) {
pthread_mutex_lock(&mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i].client_socket != 0 && clients[i].client_socket != sender_socket) {
//현재 시간 출력
time_t raw_time;
struct tm *time_info;
time(&raw_time);
time_info = localtime(&raw_time);
char formatted_time[20];
strftime(formatted_time, sizeof(formatted_time), "[%H:%M:%S]", time_info);
//포맷된 메시지 생성
char formatted_message[MAX_MESSAGE_LENGTH + 30];
sprintf(formatted_message, "%s %s", clients, message);
//클라이언트로 메시지 전송
send(clients[i].client_socket, formatted_message, strlen(formatted_message), 0);
}
}
pthread_mutex_unlock(&mutex);
}
- 시연 영상
728x90
'---포트폴리오--- > 텀 프로젝트' 카테고리의 다른 글
Raspberry Pi를 사용한 임베디드 시스템 텀 프로젝트(2) (1) | 2024.07.24 |
---|---|
Raspberry Pi를 사용한 임베디드 시스템 텀 프로젝트(1) (0) | 2024.07.23 |