#include #include #include #include #include #include #include #include #define BUF_SIZE 1024 // 에러 처리 헬퍼 void error_exit(char *msg) { perror(msg); exit(1); } void loop(int sockfd) { fd_set read_fds; char buf[BUF_SIZE]; int max_fd = sockfd > STDIN_FILENO ? sockfd : STDIN_FILENO; int stdin_eof = 0; // [NEW] 입력 종료 여부 플래그 while (1) { FD_ZERO(&read_fds); // [NEW] 입력이 아직 안 끝났을 때만 STDIN 감시 if (!stdin_eof) { FD_SET(STDIN_FILENO, &read_fds); } FD_SET(sockfd, &read_fds); // 소켓은 항상 감시 if (select(max_fd + 1, &read_fds, NULL, NULL, NULL) < 0) { if (errno == EINTR) continue; error_exit("select"); } // 1. 키보드/파일 입력 -> 소켓 전송 if (!stdin_eof && FD_ISSET(STDIN_FILENO, &read_fds)) { int n = read(STDIN_FILENO, buf, BUF_SIZE); if (n < 0) error_exit("read stdin"); if (n == 0) { // [NEW] EOF(파일 끝) 도달 시: // 즉시 종료하지 않고, "나는 보낼 거 다 보냈다"고 표시만 함 stdin_eof = 1; // (선택사항) TCP Half-Close: 서버에게 "보낼 거 끝났다"고 알림 shutdown(sockfd, SHUT_WR); } else { if (write(sockfd, buf, n) < 0) error_exit("write to socket"); } } // 2. 소켓 수신 -> 화면 출력 if (FD_ISSET(sockfd, &read_fds)) { int n = read(sockfd, buf, BUF_SIZE); if (n < 0) error_exit("read socket"); if (n == 0) { // [NEW] 서버가 연결을 끊었을 때 비로소 루프 종료! break; } if (write(STDOUT_FILENO, buf, n) < 0) error_exit("write to stdout"); } } } int main(int argc, char *argv[]) { if (argc < 3) { printf("Usage:\n"); printf(" Connect: nc \n"); printf(" Listen : nc -l \n"); return 1; } int sockfd; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; // --- Server Mode (-l) --- if (strcmp(argv[1], "-l") == 0) { int port = atoi(argv[2]); int listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd < 0) error_exit("socket"); // 주소 재사용 허용 (Time-wait 방지) int opt = 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) error_exit("bind"); if (listen(listen_fd, 1) < 0) error_exit("listen"); printf("Listening on port %d...\n", port); struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); sockfd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len); if (sockfd < 0) error_exit("accept"); printf("Connection from %s\n", inet_ntoa(client_addr.sin_addr)); close(listen_fd); // 더 이상 리스닝 안 함 (1:1 채팅) } // --- Client Mode --- else { char *ip = argv[1]; int port = atoi(argv[2]); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error_exit("socket"); addr.sin_port = htons(port); if (inet_pton(AF_INET, ip, &addr.sin_addr) <= 0) error_exit("invalid address"); printf("Connecting to %s:%d...\n", ip, port); if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) error_exit("connect"); printf("Connected!\n"); } // 데이터 교환 루프 진입 loop(sockfd); close(sockfd); return 0; }