Загружаем каталог…
Загружаем каталог…
#client.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> // 소켓 생성 및 통신 관련 함수 (socket, connect, send, recv 등) #include <arpa/inet.h> // IP 주소 변환 및 바이트 순서 변환 함수 (htons, inet_pton 등) #define PORT 8080 #define BUF_SIZE 1024 int main(void) { // 1. TCP 소켓 생성 (IPv4, TCP 통신) int sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd == -1) { perror("socket"); exit(1); } // 2. 서버 주소 정보 구조체 설정 (IPv4, Port 8080) struct sockaddr_in server_addr = { 0 }; // 쓰레기 값 방지를 위한 초기화 server_addr.sin_family = AF_INET; // IPv4 주소 체계 server_addr.sin_port = htons(PORT); // Port 번호를 네트워크 바이트 순서(Big-Endian)로 변환 // 문자열 IP 주소("127.0.0.1")를 바이너리 네트워크 주소로 변환하여 server_addr.sin_addr에 저장 if (inet_pton( AF_INET, "127.0.0.1", &server_addr.sin_addr ) != 1){ perror("inet_pton"); close(sock_fd); exit(1); } // 3. 서버에 TCP 연결 요청 (3-way handshake 수행) if (connect( sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr) ) == -1) { perror("connect"); close(sock_fd); exit(1); } printf("connected to server\n"); const char *message = "hello server"; // 4. 서버로 메시지 전송 if (send( sock_fd, message, strlen(message), 0 ) == -1) { perror("send"); close(sock_fd); exit(1); } char buffer[BUF_SIZE] = {0}; // 5. 서버로부터 응답 수신 (데이터가 올 때까지 대기/Blocking) ssize_t n = recv( sock_fd, buffer, sizeof(buffer) - 1, // 문자열 끝 null 문자를 남겨두기 위해 -1 0 ); if (n == -1) { perror("recv"); close(sock_fd); exit(1); } // 수신한 데이터 끝에 null 문자('\0')를 붙여 안전한 문자열로 완결 buffer[n] = '\0'; printf("server response: %s\n", buffer); // 6. 소켓 자원 해제 및 연결 종료 close(sock_fd); return 0; } #server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8080 #define BUF_SIZE 1024 int main(void) { int server_fd = socket(AF_INET, SOCK_STREAM, 0); if (server_fd == -1) { perror("socket"); exit(1); } //열려있다면, 무시하고 다시 덮어씀 int opt = 1; if (setsockopt( server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) ) == -1) { perror("setsockopt"); close(server_fd); exit(1); } //IPv4 주소 구조체 생성, 설정 struct sockaddr_in addr = { 0 }; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = INADDR_ANY; //설정한 주소 정보를 서버 소켓에 바인딩 if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("bind"); close(server_fd); exit(1); } //소켓을 연결 수신 대기 상태로 전환 if (listen(server_fd, 5) == -1) { perror("listen"); close(server_fd); exit(1); } printf("listening of port %d...\n", PORT); // 여기서 block int client_fd = accept(server_fd, NULL,NULL); if (client_fd == -1) { perror("accept"); close(server_fd); exit(1); } printf("client connected\n"); printf("server_fd = %d\n", server_fd); printf("client_fd = %d\n", client_fd); char buffer[BUF_SIZE] = { 0 }; ssize_t n = recv( client_fd, buffer, sizeof(buffer) - 1, 0 ); if (n == -1) { perror("recv"); close(client_fd); close(server_fd); exit(1); } buffer[n] = '\0'; printf("recieved: %s\n", buffer); const char *response = "hello client"; if (send( client_fd, response, strlen(response), 0 ) == -1) { perror("send"); } close(client_fd); close(server_fd); return 0; } 이번 server.c / client.c (B5 TCP Socket 기초) 실습에서 질문하셨던 핵심 개념과 답변들을 한눈에 복습 및 기록하실 수 있도록 요약·정리해 두었습니다. 1. sockaddr_in 구조체 멤버명 sin_family 의 의미 sin = s ocket in ternet (인터넷 소켓) family = 주소 체계 / 계열 의미: struct sockaddr_in 구조체 내부 멤버의 접두어( sin_ )이며, sin_family = AF_INET 은 IPv4 인터넷 주소 체계를 사용하겠다는 의미입니다. 2. &server_addr.sin_addr 의 역할 개념: server_addr 구조체 내부의 IP 주소 필드( sin_addr )에 대한 메모리 주소(Pointer)입니다. 역할: inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) 와 같이, 문자열 IP를 네트워크 바이너리로 변환한 결과를 어느 메모리 위치에 저장할지 넘겨주는 목적 으로 사용됩니다. 3. TCP Three-way Handshake의 주체 및 시점 주체: C 언어 코드( accept 등)가 아니라 OS 커널의 TCP/IP 스택 이 백그라운드에서 실행합니다. 시점: 서버가 listen() 을 호출해 LISTEN 상태가 된 후, 클라이언트가 connect() 를 호출할 때 일어납니다. accept() 의 실제 역할: Handshake를 직접 하는 것이 아니라, 커널 내부 Accept Queue에 이미 Handshake가 완료되어 들어온 연결을 꺼내와서 애플리케이션용 File Descriptor( client_fd )를 반환 합니다. 4. recv() / accept() 에서의 스레드 블로킹 (Blocking) 스레드 상태: 연결 요청이나 데이터가 올 때까지 스레드가 OS 커널에 의해 Waiting (Blocked) 상태로 전환 됩니다. 특징: 이 대기 상태 동안 해당 스레드는 CPU 자원을 전혀 소모하지 않으며 , 커널에 이벤트(패킷 수신 등)가 발생하면 깨어나 동작을 재개합니다. 5. TCP MSS와 Sequence Number 관리 개념: TCP 통신에서 MSS(최대 세그먼트 크기) 협상과 Sequence/ACK Number 를 통한 데이터 순서 보장 및 손실 재전송 제어는 TCP의 핵심 동작 방식입니다. 담당 영역: 이 모든 과정 역시 개발자가 직접 코딩하는 것이 아니라 OS 커널이 자동으로 관리 하며, C 소켓 API( send , recv )는 그 결과를 애플리케이션으로 가져오는 인터페이스 역할을 합니다. 6. TCP의 Byte Stream 특성과 가상 예시 개념: TCP는 메시지 경계(Boundary)가 없는 '연속된 바이트의 흐름(Byte Stream)'입니다. send() 2번 예시의 의미: 작성하신 클라이언트 코드는 send() 를 1번만 부르고 있으나, "만약 클라이언트가 send() 를 2번 호출한다면?"이라는 가상의 예시입니다. 결과: 클라이언트가 send() 를 2번 나누어 부르더라도 네트워크 상황이나 OS 버퍼 상태에 따라 서버는 recv() 1번으로 통째로 받거나 여러 번 쪼개서 받을 수 있습니다. 따라서 추후 Protocol Framing(구분자 추가, 길이 명시 등) 처리 로직이 필요합니다. 7. recv() 호출 시 sizeof(buffer) - 1 을 사용하는 이유 목적: 수신된 데이터 맨 끝에 C언어 문자열 종료 문자인 NULL( '\0' )을 추가할 1바이트 공간을 확보 하기 위함입니다. 이유: recv() 는 데이터 뒤에 NULL 문자를 자동으로 붙여주지 않습니다. 만약 버퍼 크기 전체(1024)를 수신받으면 buffer[n] = '\0' 시 메모리 침범(Buffer Overflow)이 발생하므로, 최대 1023바이트만 받도록 제한하여 안전하게 문자열로 완결 짓습니다. 8. recv() 함수의 추상화 수준 개념: recv() 는 하드웨어(NIC) 인터럽트, IP/TCP 헤더 검증, Checksum, 패킷 재정렬, 손실 재전송, 커널 ➔ 유저 메모리 복사 등 복잡한 네트워크 스택의 처리를 C 함수 호출 한 줄로 은닉한 고급 추상화 API 입니다.
То, что RADAR обнаружил и классифицировал для этой возможности. Это опубликованный источником текст, а не подтверждение, что предложение ещё действует.
[os]tcp server. #client.c #include #include #include #include #include // 소켓 생성 및 통신 관련 함수 (socket, connect, send, recv 등) #include // IP 주소 변환 및 바이트 순서 변환 함수 (htons, inet_pton 등) #define PORT 8080 #define BUF_SIZE 1024 int main(void) { // 1. TCP 소켓 생성 (IPv4, TCP 통신) int sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd == -1) { perror("socket"); exit(1); } // 2. 서버 주소 정보 구조체 설정 (IPv4, Port…
Открыть источник