🌟 다중 접속 로그인 구현
더보기

//client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#define BUF_SIZE 1024
char global_pw[50] = "passw123";
char global_id[50] = "user1";
pthread_mutex_t input_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t output_mutex = PTHREAD_MUTEX_INITIALIZER;
// 상태 관리용 enum
typedef enum {
STATE_WAIT_LOGIN,
STATE_LOGGED_IN
} ClientState;
ClientState client_state = STATE_WAIT_LOGIN;
void error_handling(const char* msg1, const char* msg2);
int login_process(int sock);
void* handle_server_response(void* arg);
void show_menu(int sock);
void change_id(int sock);
void change_pw(int sock);
void login_end();
int send_login(int sock, const char* id, const char* pw);
typedef struct {
int sock;
int* connection_active;
} ThreadArg;
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("사용법: %s <IP> <PORT>\n", argv[0]);
exit(1);
}
char* ip = argv[1];
int port = atoi(argv[2]);
int sock;
struct sockaddr_in serv_addr;
char id[50], pw[50];
int connection_active = 1;
pthread_t response_thread;
ThreadArg thread_arg;
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_port = htons(port);
serv_addr.sin_addr.s_addr = inet_addr(ip);
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("연결 실패:", "connect() error");
printf("서버에 연결되었습니다. (%s:%d)\n", ip, port);
thread_arg.sock = sock;
thread_arg.connection_active = &connection_active;
pthread_create(&response_thread, NULL, handle_server_response, &thread_arg);
while (connection_active) {
if (client_state == STATE_WAIT_LOGIN) {
if (!login_process(sock)) break;
} else if (client_state == STATE_LOGGED_IN) {
show_menu(sock);
client_state = STATE_WAIT_LOGIN;
}
usleep(100000);
}
pthread_join(response_thread, NULL);
close(sock);
printf("연결이 종료되었습니다.\n");
return 0;
}
int login_process(int sock) {
char id[50], pw[50];
pthread_mutex_lock(&input_mutex);
printf("\n=== 로그인 ===\n");
printf("ID 입력: "); fflush(stdout);
if (scanf("%49s", id) != 1) {
pthread_mutex_unlock(&input_mutex);
return 0;
}
printf("PW 입력: "); fflush(stdout);
if (scanf("%49s", pw) != 1) {
pthread_mutex_unlock(&input_mutex);
return 0;
}
pthread_mutex_unlock(&input_mutex);
// 전역 변수에 저장
strcpy(global_id, id);
strcpy(global_pw, pw);
return send_login(sock, id, pw);
}
void* handle_server_response(void* arg) {
ThreadArg* thread_arg = (ThreadArg*)arg;
int sock = thread_arg->sock;
int* connection_active = thread_arg->connection_active;
char buffer[BUF_SIZE];
ssize_t len;
while (*connection_active) {
len = read(sock, buffer, BUF_SIZE - 1);
if (len <= 0) {
pthread_mutex_lock(&output_mutex);
printf("\n서버 연결이 끊어졌습니다.\n");
pthread_mutex_unlock(&output_mutex);
*connection_active = 0;
break;
}
buffer[len] = '\0';
pthread_mutex_lock(&output_mutex);
printf("\n");
if (strcmp(buffer, "새 ID 입력:") == 0 || strcmp(buffer, "새 PW 입력:") == 0) {
printf("%s", buffer); // 같은 줄에 출력
fflush(stdout);
} else if (strcmp(buffer, "ID 변경 완료") == 0 || strcmp(buffer, "PW 변경 완료") == 0) {
printf("\n%s\n", buffer); // 줄바꿈 포함
fflush(stdout);
} else {
printf("\n%s\n", buffer);
}
pthread_mutex_unlock(&output_mutex);
if (strcmp(buffer, "로그인 성공") == 0) {
client_state = STATE_LOGGED_IN;
} else if (strcmp(buffer, "로그인 실패") == 0) {
client_state = STATE_WAIT_LOGIN;
}
}
return NULL;
}
int send_login(int sock, const char* id, const char* pw) {
char message[BUF_SIZE];
snprintf(message, sizeof(message), "%s:%s", id, pw);
int message_len = strlen(message) + 1;
int net_len = htonl(message_len);
write(sock, &net_len, sizeof(net_len));
write(sock, message, message_len);
return 1;
}
void show_menu(int sock) {
while (1) {
int choice;
pthread_mutex_lock(&input_mutex);
printf("\n=== 로그인 메뉴 ===\n");
printf("1. ID 변경\n2. PW 변경\n3. 종료\n");
printf("선택: ");
if (scanf("%d", &choice) != 1) {
pthread_mutex_unlock(&input_mutex);
break;
}
getchar();
pthread_mutex_unlock(&input_mutex);
if (choice == 1) {
write(sock, "CHANGE_ID", strlen("CHANGE_ID") + 1);
change_id(sock);
login_process(sock);
usleep(200000);
} else if (choice == 2) {
write(sock, "CHANGE_PW", strlen("CHANGE_PW") + 1);
change_pw(sock);
login_process(sock);
usleep(200000);
} else if (choice == 3) {
write(sock, "EXIT", strlen("EXIT") + 1);
login_end();
} else {
pthread_mutex_lock(&output_mutex);
printf("1~3 사이의 숫자를 입력하세요.\n");
pthread_mutex_unlock(&output_mutex);
}
}
}
void change_id(int sock) {
char new_id[50];
pthread_mutex_lock(&input_mutex);
scanf("%s", new_id);
pthread_mutex_unlock(&input_mutex);
write(sock, new_id, strlen(new_id) + 1);
strcpy(global_id, new_id);
// 💡 서버에서 "ID 변경 완료" 메시지를 먼저 도착시킬 시간 확보
usleep(150000); // 0.15초
client_state = STATE_WAIT_LOGIN;
}
void change_pw(int sock) {
char new_pw[50];
pthread_mutex_lock(&input_mutex);
scanf("%s", new_pw);
pthread_mutex_unlock(&input_mutex);
write(sock, new_pw, strlen(new_pw) + 1);
strcpy(global_pw, new_pw);
// 💡 서버에서 "ID 변경 완료" 메시지를 먼저 도착시킬 시간 확보
usleep(150000); // 0.15초
client_state = STATE_WAIT_LOGIN;
}
void login_end() {
printf("\n서버 연결을 종료합니다.\n");
exit(0);
}
void error_handling(const char* msg1, const char* msg2) {
fputs(msg1, stderr);
fputs(" ", stderr);
fputs(msg2, stderr);
fputc('\n', stderr);
exit(1);
}
// login_server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#define BUF_SIZE 1024
#define MAX_CLIENTS 100
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int client_count = 0;
void error_handling(const char* msg1, const char* msg2);
void* handle_client(void* arg);
void change_id(int client_sock);
void change_pw(int client_sock);
int receive_login(int client_sock, char* recv_id, char* recv_pw);
typedef struct {
char Valid_id[50];
char Valid_pw[50];
} Account;
typedef struct {
int client_sock;
struct sockaddr_in client_addr;
int client_id;
} ClientInfo;
Account valid_user = { "user1", "passw123" };
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("사용법: %s <PORT>\n", argv[0]);
exit(1);
}
int port = atoi(argv[1]);
int serv_sock, client_sock;
struct sockaddr_in serv_addr, client_addr;
socklen_t client_addr_size;
pthread_t client_thread;
ClientInfo* client_info;
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
error_handling("소켓 생성 실패:", "socket() error");
// SO_REUSEADDR 옵션 설정
int opt = 1;
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind 실패:", "bind() error");
if (listen(serv_sock, 5) == -1)
error_handling("listen 실패:", "listen() error");
printf("멀티스레드 서버 대기 중... (포트 %d)\n", port);
printf("최대 %d개의 클라이언트 동시 연결 가능\n", MAX_CLIENTS);
while (1) {
client_addr_size = sizeof(client_addr);
client_sock = accept(serv_sock, (struct sockaddr*)&client_addr, &client_addr_size);
if (client_sock == -1) {
perror("accept 실패");
continue;
}
pthread_mutex_lock(&mutex);
if (client_count >= MAX_CLIENTS) {
pthread_mutex_unlock(&mutex);
printf("최대 클라이언트 수 초과, 연결 거부\n");
close(client_sock);
continue;
}
client_count++;
int current_client_id = client_count;
pthread_mutex_unlock(&mutex);
// 클라이언트 정보 구조체 생성
client_info = malloc(sizeof(ClientInfo));
client_info->client_sock = client_sock;
client_info->client_addr = client_addr;
client_info->client_id = current_client_id;
printf("클라이언트 #%d 연결됨 (%s:%d)\n",
current_client_id,
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
// 클라이언트 처리 스레드 생성
if (pthread_create(&client_thread, NULL, handle_client, (void*)client_info) != 0) {
perror("스레드 생성 실패");
close(client_sock);
free(client_info);
pthread_mutex_lock(&mutex);
client_count--;
pthread_mutex_unlock(&mutex);
continue;
}
// 스레드를 detach하여 자동으로 정리되도록 함
pthread_detach(client_thread);
}
close(serv_sock);
return 0;
}
void* handle_client(void* arg) {
ClientInfo* client_info = (ClientInfo*)arg;
int client_sock = client_info->client_sock;
int client_id = client_info->client_id;
char recv_id[50], recv_pw[50];
char command[BUF_SIZE];
ssize_t len;
printf("[클라이언트 #%d] 처리 시작\n", client_id);
while (1) {
// 로그인 정보 수신
if (!receive_login(client_sock, recv_id, recv_pw)) {
printf("[클라이언트 #%d] 로그인 정보 수신 실패\n", client_id);
break;
}
printf("[클라이언트 #%d] 로그인 시도: ID=%s\n", client_id, recv_id);
// 로그인 검증 (뮤텍스로 보호)
pthread_mutex_lock(&mutex);
int login_success = (strcmp(recv_id, valid_user.Valid_id) == 0 &&
strcmp(recv_pw, valid_user.Valid_pw) == 0);
pthread_mutex_unlock(&mutex);
if (login_success) {
write(client_sock, "로그인 성공", strlen("로그인 성공") + 1);
printf("[클라이언트 #%d] 로그인 성공\n", client_id);
// 로그인 성공 후 명령 처리 루프
while (1) {
memset(command, 0, BUF_SIZE);
len = read(client_sock, command, BUF_SIZE);
if (len <= 0) {
printf("[클라이언트 #%d] 연결 종료\n", client_id);
break; // 세션 종료
}
printf("[클라이언트 #%d] 명령 수신: %s\n", client_id, command);
if (strcmp(command, "CHANGE_ID") == 0) {
change_id(client_sock);
printf("[클라이언트 #%d] ID 변경 완료\n", client_id);
break; // 🔁 다시 로그인 루프로 진입
} else if (strcmp(command, "CHANGE_PW") == 0) {
change_pw(client_sock);
printf("[클라이언트 #%d] PW 변경 완료\n", client_id);
break; // 🔁 다시 로그인 루프로 진입
} else if (strcmp(command, "EXIT") == 0) {
printf("[클라이언트 #%d] 클라이언트 종료 요청\n", client_id);
goto END_SESSION;
} else {
printf("[클라이언트 #%d] 알 수 없는 명령: %s\n", client_id, command);
continue;
}
}
} else {
write(client_sock, "로그인 실패", strlen("로그인 실패") + 1);
printf("[클라이언트 #%d] 로그인 실패\n", client_id);
continue; // 🔁 다시 로그인 시도
}
}
END_SESSION:
// 클라이언트 연결 종료 처리
close(client_sock);
printf("[클라이언트 #%d] 연결 해제\n", client_id);
pthread_mutex_lock(&mutex);
client_count--;
pthread_mutex_unlock(&mutex);
free(client_info);
return NULL;
}
int receive_login(int client_sock, char* recv_id, char* recv_pw) {
char buffer[BUF_SIZE];
int message_len;
ssize_t len, total = 0;
// 메시지 길이 수신
while (total < sizeof(message_len)) {
len = read(client_sock, (char*)&message_len + total, sizeof(message_len) - total);
if (len <= 0) return 0;
total += len;
}
message_len = ntohl(message_len);
if (message_len >= BUF_SIZE) return 0;
// 메시지 수신
total = 0;
while (total < message_len) {
len = read(client_sock, buffer + total, message_len - total);
if (len <= 0) return 0;
total += len;
}
buffer[message_len - 1] = '\0';
// ID:PW 파싱
char* colon = strchr(buffer, ':');
if (!colon) return 0;
*colon = '\0';
strncpy(recv_id, buffer, 49);
recv_id[49] = '\0';
strncpy(recv_pw, colon + 1, 49);
recv_pw[49] = '\0';
return 1;
}
void change_id(int client_sock) {
pthread_mutex_lock(&mutex);
char new_id[50];
ssize_t len;
write(client_sock, "새 ID 입력:", strlen("새 ID 입력:") + 1);
len = read(client_sock, new_id, sizeof(new_id) - 1);
if (len <= 0) {
pthread_mutex_unlock(&mutex);
return;
}
new_id[len] = '\0';
// 개행 제거
char* newline = strchr(new_id, '\n');
if (newline) *newline = '\0';
strncpy(valid_user.Valid_id, new_id, sizeof(valid_user.Valid_id) - 1);
valid_user.Valid_id[sizeof(valid_user.Valid_id) - 1] = '\0';
printf("ID가 [%s]로 변경되었습니다.\n", valid_user.Valid_id);
write(client_sock, "ID 변경 완료", strlen("ID 변경 완료") + 1);
pthread_mutex_unlock(&mutex);
}
void change_pw(int client_sock) {
pthread_mutex_lock(&mutex);
char new_pw[50];
ssize_t len;
write(client_sock, "새 pw 입력:", strlen("새 pw 입력:") + 1);
len = read(client_sock, new_pw, sizeof(new_pw) - 1);
if (len <= 0) {
pthread_mutex_unlock(&mutex);
return;
}
new_pw[len] = '\0';
char* newline = strchr(new_pw, '\n');
if (newline) *newline = '\0';
strncpy(valid_user.Valid_pw, new_pw, sizeof(valid_user.Valid_pw) - 1);
valid_user.Valid_pw[sizeof(valid_user.Valid_pw) - 1] = '\0';
printf("PW가 [%s]로 변경되었습니다.\n", valid_user.Valid_pw);
write(client_sock, "PW 변경 완료", strlen("PW 변경 완료") + 1);
pthread_mutex_unlock(&mutex);
}
void error_handling(const char* msg1, const char* msg2) {
fputs(msg1, stderr);
fputs(" ", stderr);
fputs(msg2, stderr);
fputc('\n', stderr);
exit(1);
}

'C > Study' 카테고리의 다른 글
C언어 Newwork Study 4 (1) | 2025.07.11 |
---|---|
C언어 Newwork Study 3 (0) | 2025.07.11 |
C언어 Newwork Study 2 (1) | 2025.07.11 |
C언어 Newwork Study 1 (0) | 2025.06.30 |