C언어 Newwork Study 5

2025. 7. 11. 23:38·C/Study

 
 
 
 
 
 
 
 
 
 
 

🌟 다중 접속 로그인 구현

더보기
//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
'C/Study' 카테고리의 다른 글
  • C언어 Newwork Study 4
  • C언어 Newwork Study 3
  • C언어 Newwork Study 2
  • C언어 Newwork Study 1
eull
eull
eull 님의 블로그 입니다.
  • eull
    eull 님의 블로그
    eull
  • 전체
    오늘
    어제
    • 개발 환경 (32) N
      • Qt (1) N
        • API (0)
        • Project (1) N
      • MYSQL_Workbench (1)
        • setting (1)
      • Linux_Ubuntu (2)
        • Task (1)
        • Setting (1)
      • C (19)
        • Concept (4)
        • Task (8)
        • Project (1)
        • Study (5)
        • Setting (1)
      • C++ (1)
        • Study (0)
        • Concept (1)
      • Python (6)
        • Task (4)
        • Project (2)
      • 일상 (1)
      • Setting (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
eull
C언어 Newwork Study 5
상단으로

티스토리툴바