얇지만 얇지 않은 TCP/IP 소켓 프로그래밍 C 2판 (TCP/IP Sockets in C 2/e, Morgan Kaufmann) 마이클 도나후(Michael J. Donahoo) 케네스 칼버트(Kenneth L. Calvert)
Chapter 02 Basic TCP Sockets 2.1 IPv4 TCP 클라이언트 2.2 IPv4 TCP 서버 2.3 소켓의 생성과 해지 2.4 주소 지정 2.5 소켓에 연결 2.6 소켓을 주소와 바인딩하기 2.7 클라이언트의 연결 요청 처리 2.8 데이터 주고받기 2.9 IPv6의 사용
소켓(Socket) 소켓이란 무엇인가? 소켓의 구분 응용 프로그램이 데이터를 주고 받는 추상화 개념(자료 구조) 네트워크 플러그인 인터페이스 TCP/IP를 포함하여 다양한 프로토콜 인터페이스 지원 소켓의 구분 프로토콜(TCP, UDP혹은 기타 프로토콜), 주소, 포트 별로 구분 하나의 응용프로그램이 여러 개의 소켓을 가질 수 있으며 반대로 여러 개의 프로그램이 하나의 소켓의 공유가 가능
소켓 통신 과정 간략화한 소켓 통신 과정 소켓 식별자 생성 소켓에 주소 정보 할당 소켓 연결 데이터 전송 TCP or UDP IP address, port number 소켓 연결 클라이언트 소켓과 서버 소켓 연결 bind(), listen(), connect(), accept() 데이터 전송 send(), recv()
int socket(int family,int type,int proto); TCP/IP 소켓의 생성 소켓 생성 어떠한 소켓을 생성할 것인가를 명시(프로토콜 종류) TCP/IP 소켓의 경우 Socket 식별자 UNIX의 파일 식별자와 동일 Windows의 경우, WinSock에서 사용하는 소켓 핸들 Windows의 경우, 파일 핸들과 같지 않음 반환 값: 소켓 식별자인 양의 정수, 에러의 경우 ‘-1’ int socket(int family,int type,int proto); Family Type Protocol TCP PF_INET SOCK_STREAM IPPROTO_TCP UDP SOCK_DGRAM IPPROTO_UDP
Data structure for file 0 Data structure for file 1 TCP/IP 소켓 식별자 유닉스/리눅스 계열에서 식별자 공간 Descriptor Table 1 2 3 4 Data structure for file 0 Data structure for file 1 Family: PF_INET Service: SOCK_STREAM Local IP: 111.22.3.4 Remote IP: 123.45.6.78 Local Port: 2249 Remote Port: 3726
TCP/IP 소켓의 주소 지정 struct sockaddr 사용 여러 가지 프로토콜을 사용하기 때문에 1)프로토콜 종류, 2)주소를 지정해야 함 TCP/IP의 경우는 인터넷 주소임을 알리는 AF_INET, IP 주소, Port번호가 필요 IP : IPv4 주소 형식과 IPv6 주소 형식으로 나뉨 Ports : TCP/UDP 관계없이 0~65535 사이의 값 사용 well-known (port 0-1023) dynamic or private (port 1024-65535)
TCP/IP 소켓의 주소 지정 범용(Generic) 소켓 주소 구조체 IPv4에 사용되는 소켓 주소 구조체 struct sockaddr { unsigned short sa_family; /* Address family (e.g., AF_INET) */ char sa_data[14]; /* Protocol-specific address information */ }; IPv4에 사용되는 소켓 주소 구조체 struct sockaddr_in unsigned short sin_family; /* Internet protocol (AF_INET) */ unsigned short sin_port; /* Port (16-bits) */ struct in_addr sin_addr; /* Internet address (32-bits) */ char sin_zero[8]; /* Not used */ struct in_addr unsigned long s_addr; /* Internet address (32-bits) */ sockaddr Family Blob 2 bytes 2 bytes 4 bytes 8 bytes sockaddr_in Family Port Internet address Not used
TCP/IP 소켓의 주소 지정 IPv6에 사용되는 소켓 주소 구조체 모든 종류의 sockaddr을 수용하기 위한 구조체 struct sockaddr_in6{ sa_family_t sin6_family; // Internet protocol(AF_INET6) in_port_t sin6_port; // Address port(16bits) uint32_t sin6_flowinfo; // Flow information struct in6_addr sin6_addr; // IPv6 address(128bits) uint32_t sin6_scope_id; // Scope identifier }; struct in_addr{ uint32_t s_addr[16]; // Internet address(128bits) 모든 종류의 sockaddr을 수용하기 위한 구조체 struct sockaddr_storage { u_char ss_len; u_char ss_family; u_char padding[128 - 2];
주소 정보를 소켓에 할당 bind()를 사용하여 주소 정보를 생성된 소켓에 할당 성공 시 ‘0’, 실패 시 ‘-1’ int bind( int sockfd, struct sockaddr *localaddr, int addrlen); int mysock,err; struct sockaddr_in myaddr; char* servIP; /* ex)203.252.164.143 */ mysock = socket(PF_INET,SOCK_STREAM,0); myaddr.sin_family = AF_INET; myaddr.sin_port = htons( portnum ); myaddr.sin_addr.s_addr = inet_addr(servIP); err=bind(mysock, (sockaddr *) &myaddr, sizeof(myaddr));
호스트 바이트 순서 Big-Endian Little-Endian 0x12 0x78 0x34 0x56 0x56 0x34 0x78 12 번지 0x34 0x56 13 번지 0x56 0x34 14 번지 0x78 0x12 15 번지
데이터 표현 방식에 따른 문제점 Big-Endian Little-Endian 0x1234 0x3412 0x12 0x12 12 번지 0x34 0x34 13 번지 0x1234 0x3412
1. 네트워크 바이트 순서는 Big-Endian 방식을 적용하기로 약속 0x34 0x12 Big-Endian 0x12 0x34 Little-Endian 0x12 0x34 12 번지 0x34 0x12 13 번지 0x1234 0x1234
바이트 순서 변환 함수 unsigned short htons(unsigned short); unsigned short ntohs(unsigned short); unsigend long htonl(unsigned long); unsigned long ntohl(unsigned long); ‘h’ : host byte order ‘n’ : network byte order ‘s’ : short (16 bit) ‘l’ : long (32 bit)
인터넷 주소 조작 함수 unsigned long inet_addr(const char * string) 1. FROM Dotted-Decimal Notation TO Big-Endian 32 비트 정수형 데이터 (Network) unsigned long inet_addr(const char * string) 2. FROM Big-Endian 32 비트 정수형 데이터 (Network) TO Dotted-Decimal Notation char * inet_ntoa(struct in_addr addr)
인터넷 주소 초기화 Template 1: struct sockaddr_in addr; 2: char *serv_ip="211.217.168.13"; 3: char *serv_port="9190"; 4: memset(&addr, 0, sizeof(addr_len)); 5: addr.sin_family = AF_INET; 6: addr.sin_addr.s_addr = inet_addr(serv_ip); 7: addr.sin_port = htons(atoi(serv_port));
클라이언트 – 서버 통신 클라이언트가 먼저 서버에게 연결 요청 클라이언트는 서버에 연결 서버의 프로세스는 미리 소켓을 열고 대기하고 있어야 함 서버는 특정 포트를 열고 대기하여야 하며 클라이언트는 이 포트를 알고 있어야 함 클라이언트는 서버에 연결 이때 클라이언트는 서버의 IP, Port 정보를 응용 프로그램에게 명시하여 접속 가능
UDP 연결 흐름도 서버는 이 부분에서 멈추고 클라이언트의 연결 요청을 기다림
TCP 연결 흐름도 연결 요청 다음 클라이언트로부터 연결 요청을 기다림
int listen(int socket, int queuelimit) TCP와 같은 연결 지향 서버에 사용 소켓의 상태를 대기 상태로 바꿈 socket: 생성된 소켓의 식별자 queuelimit : 연결을 수행중에 다른 연결이 들어오면 연결 요청을 queue에 넣고 보류, 이때 사용하는 queue의 크기 int listen(int socket, int queuelimit)
서버의 역할과 연결요청 대기상태 서버 소켓은 일종의 ‘문지기’ 이다.
서버의 연결 대기 함수- Accept() int accept(int socket, struct sockaddr *clientdaddress, int addr_len) listen()호출 후, accept()를 수행하면 클라이언트의 연결 요청(connect())에 대해 응답합 passive open 클라이언트와 데이터 송수신(send/recv)이 가능한 새로운 소켓 식별자를 반환
클라이언트의 연결 함수 - Connect() int connect(int socket, struct sockaddr *foreignAddress, int addr_len) 클라이언트는 connect()를 호출하여 연결의 상태를 ‘active open’으로 만듬 foreignAddress는 서버의 IP, port를 담고 있는 주소 구조체
Send(to), Recv(from) 연결이 이루어진 후에는 send/recv를 이용하여 데이터의 송수신이 가능 int send(int socket, char *message, int msg_len, int flags) 주어진 소켓을 통하여 messge의 송신이 가능 int recv(int scoket, char *buffer, int buf_len, int flags) 주어진 소켓을 통해 주어진 버퍼에 데이터를 수신
클라이언트와 서버의 통신 클라이언트 : 연결을 초기화 하는 주체 Client: Bob Server: Jane 서버 : 수동적으로 연결을 기다림 Client: Bob “Hi. I’m Bob.” Server: Jane “Hi, Bob. I’m Jane” “Nice to meet you, Jane.”
서버는 클라이언트의 연결을 받아들일 준비를 하고 시작 TCP 상의 서버/클라이언트 통신 서버는 클라이언트의 연결을 받아들일 준비를 하고 시작 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 TCP 소켓 생성 TCP 소켓 생성 소켓에 포트 할당 연결 설정 /* Create socket for incoming connections */ if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithSystemMessage("socket() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 TCP 소켓 생성 TCP 소켓 생성 소켓에 포트 할당 연결 설정 echoServAddr.sin_family = AF_INET; /* Internet address family */ echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);/* Any incoming interface */ echoServAddr.sin_port = htons(echoServPort); /* Local port */ if (bind(servSock,(struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithSystemMessage("bind() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 /* Mark the socket so it will listen for incoming connections */ if (listen(servSock, MAXPENDING) < 0) DieWithSystemMessage("listen() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 TCP 소켓 생성 TCP 소켓 생성 소켓에 포트 할당 연결 설정 for (;;) /* Run forever */ { clntLen = sizeof(echoClntAddr); if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen)) < 0) DieWithError("accept() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버는 이 시점에서 클라이언트의 연결을 처리하기 위해서 대기 클라이언트는 서버에 연결 시도 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 /* Create a reliable, stream socket using TCP */ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithSystemMessage("socket() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 TCP 소켓 생성 TCP 소켓 생성 소켓에 포트 할당 연결 설정 echoServAddr.sin_family = AF_INET; /* Internet address family */ echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* Server IP address */ echoServAddr.sin_port = htons(echoServPort); /* Server port */ if (connect(sock,(struct sockaddr *)&echoServAddr, sizeof(echoServAddr)) < 0) DieWithSystemMessage ("connect() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen)) < 0) DieWithError("accept() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 echoStringLen = strlen(echoString); /* Determine input length */ /* Send the string to the server */ if (send(sock, echoString, echoStringLen, 0) != echoStringLen) DieWithUserMessage ("send() sent a different number of bytes than expected"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 /* Receive message from client */ if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) DieWithSystemMessage("recv() failed"); 클라이언트 TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 상의 서버/클라이언트 통신 서버 클라이언트 close(sock); close(clntSocket) TCP 소켓 생성 연결 설정 데이터 송수신 연결 종료 서버 TCP 소켓 생성 소켓에 포트 할당 소켓 상태를 대기(listen)로 변경 다음을 반복적으로 수행 새로운 연결을 받아들임 데이터 송수신 연결을 종료 Students should follow along with source code in the book
TCP 데이터 교환 클라이언트는 사전에 서버의 주소 정보(IP, port)를 알아야함 서버는 클라이언트가 접속할 포트만 정하고 있음 send()와 recv() 간에는 어떠한 정해진 룰이 없음 Client send(“Hello Bob”) recv() -> “Hi Jane” Server recv() -> “Hello ” recv() -> “Bob” send(“Hi ”) send(“Jane”) The client must know the server address and port The server discovers the client’s address and port at connection
연결 종료 연결을 종료하기 위해서 close()를 사용 파일의 EOF와 유사 Echo Client Echo Server send(string) while (not received entire string) recv(buffer) print(buffer) close(socket) Echo Server while(client has not closed connection) send(buffer) close(client socket) In echo application, how does server know when client is done considering it doesn’t know echo string length? Role of closer can (and is in HTTP) be reversed
TCPEchoClient4.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/socket.h> 7 #include <netinet/in.h> 8 #include <arpa/inet.h> 9 #include "Practical.h" 10 11 int main(int argc, char *argv[]) { 12 13 if (argc < 3 || argc > 4) // Test for correct number of arguments 14 DieWithUserMessage("Parameter(s)", 15 "<Server Address> <Echo Word> [<Server Port>]"); 16 17 char *servIP = argv[1]; // First arg: server IP address (dotted quad) 18 char *echoString = argv[2]; // Second arg: string to echo 19 20 // Third arg (optional): server port (numeric). 7 is well-known echo port 21 in_port_t servPort = (argc == 4) ? atoi(argv[3]) : 7; 22 23 // Create a reliable, stream socket using TCP 24 int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 25 if (sock < 0) 26 DieWithSystemMessage("socket() failed");
TCPEchoClient4.c 27 28 // Construct the server address structure 29 struct sockaddr_in servAddr; // Server address 30 memset(&servAddr, 0, sizeof(servAddr)); // Zero out structure 31 servAddr.sin_family = AF_INET; // IPv4 address family 32 // Convert address 33 int rtnVal = inet_pton(AF_INET, servIP, &servAddr.sin_addr.s_addr); 34 if (rtnVal == 0) 35 DieWithUserMessage("inet_pton() failed", "invalid address string"); 36 else if (rtnVal < 0) 37 DieWithSystemMessage("inet_pton() failed"); 38 servAddr.sin_port = htons(servPort); // Server port 39 40 // Establish the connection to the echo server 41 if (connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) 42 DieWithSystemMessage("connect() failed"); 43 44 size_t echoStringLen = strlen(echoString); // Determine input length 45 46 // Send the string to the server 47 ssize_t numBytes = send(sock, echoString, echoStringLen, 0); 48 if (numBytes < 0) 49 DieWithSystemMessage("send() failed"); 50 else if (numBytes != echoStringLen) 51 DieWithUserMessage("send()", "sent unexpected number of bytes");
TCPEchoClient4.c 52 53 // Receive the same string back from the server 54 unsigned int totalBytesRcvd = 0; // Count of total bytes received 55 fputs("Received: ", stdout); // Setup to print the echoed string 56 while (totalBytesRcvd < echoStringLen) { 57 char buffer[BUFSIZE]; // I/O buffer 58 /* Receive up to the buffer size (minus 1 to leave space for 59 a null terminator) bytes from the sender */ 60 numBytes = recv(sock, buffer, BUFSIZE - 1, 0); 61 if (numBytes < 0) 62 DieWithSystemMessage("recv() failed"); 63 else if (numBytes == 0) 64 DieWithUserMessage("recv()", "connection closed prematurely"); 65 totalBytesRcvd += numBytes; // Keep tally of total bytes 66 buffer[numBytes] = '\0'; // Terminate the string! 67 fputs(buffer, stdout); // Print the echo buffer 68 } 69 70 fputc('\n', stdout); // Print a final linefeed 71 72 close(sock); 73 exit(0); 74 }
TCPEchoServer4.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <sys/types.h> 5 #include <sys/socket.h> 6 #include <netinet/in.h> 7 #include <arpa/inet.h> 8 #include "Practical.h" 9 10 static const int MAXPENDING = 5; // Maximum outstanding connection requests 11 12 int main(int argc, char *argv[]) { 13 14 if (argc != 2) // Test for correct number of arguments 15 DieWithUserMessage("Parameter(s)", "<Server Port>"); 16 17 in_port_t servPort = atoi(argv[1]); // First arg: local port 18 19 // Create socket for incoming connections 20 int servSock; // Socket descriptor for server 21 if ((servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 22 DieWithSystemMessage("socket() failed"); 23
TCPEchoServer4.c 24 // Construct local address structure 25 struct sockaddr_in servAddr; // Local address 26 memset(&servAddr, 0, sizeof(servAddr)); // Zero out structure 27 servAddr.sin_family = AF_INET; // IPv4 address family 28 servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // Any incoming interface 29 servAddr.sin_port = htons(servPort); // Local port 30 31 // Bind to the local address 32 if (bind(servSock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0) 33 DieWithSystemMessage("bind() failed"); 34 35 // Mark the socket so it will listen for incoming connections 36 if (listen(servSock, MAXPENDING) < 0) 37 DieWithSystemMessage("listen() failed"); 38 39 for (;;) { // Run forever 40 struct sockaddr_in clntAddr; // Client address 41 // Set length of client address structure (in-out parameter) 42 socklen_t clntAddrLen = sizeof(clntAddr); 43 44 // Wait for a client to connect 45 int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen); 46 if (clntSock < 0) 47 DieWithSystemMessage("accept() failed");
TCPEchoServer4.c 48 49 // clntSock is connected to a client! 50 51 char clntName[INET_ADDRSTRLEN]; // String to contain client address 52 if (inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, 53 sizeof(clntName)) != NULL) 54 printf("Handling client %s/%d\n", clntName, ntohs(clntAddr.sin_port)); 55 else 56 puts("Unable to get client address"); 57 58 HandleTCPClient(clntSock); 59 } 60 // NOT REACHED 61 }
HandleTCPClient() 1 void HandleTCPClient(int clntSocket) { 2 char buffer[BUFSIZE]; // Buffer for echo string 3 4 // Receive message from client 5 ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); 6 if (numBytesRcvd < 0) 7 DieWithSystemMessage("recv() failed"); 8 9 // Send received string and receive again until end of stream 10 while (numBytesRcvd > 0) { // 0 indicates end of stream 11 // Echo message back to client 12 ssize_t numBytesSent = send(clntSocket, buffer, numBytesRcvd, 0); 13 if (numBytesSent < 0) 14 DieWithSystemMessage("send() failed"); 15 else if (numBytesSent != numBytesRcvd) 16 DieWithUserMessage("send()", "sent unexpected number of bytes"); 17 18 // See if there is more data to receive 19 numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); 20 if (numBytesRcvd < 0) 21 DieWithSystemMessage("recv() failed"); 22 } 23 24 close(clntSocket); // Close client socket 25 }
컴파일 방법 – 리눅스 환경 Native 리눅스/VMware 리눅스/ Cygwin 환경 유닉스 기반 리눅스 기반 $ gcc <컴파일 옵션> -o <실행파일> <소스파일들> -lsocket –lnsl 리눅스 기반 $ gcc <컴파일 옵션> -o <실행파일> <소스파일들>
컴파일 방법 – 윈도우 환경 Visual Studio(WinSock) 기타 시스템 관련 함수들은 버클리 소켓과 완전 동일하지 않다 #include <stdio.h> #include <winsock.h> #include <stdlib.h> void main() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { fprintf(stderr, "WSAStartup() failed"); exit(1); } … closesocket(sock);
컴파일 방법 – 윈도우 환경 윈속 라이브러리 추가(wsock32.lib) menu->project->settings->link->wsock32.lib */
과제 앞의 client 코드와 Server 코드를 컴파일하여 동작을 확인하라.
과제 기본 과제 앞의 예제를 수정하여 다음의 기능을 가지는 프로그램을 작성하라. 서버는 클라이언트의 문자열을 화면에 출력한다. 서버는 클라이언트의 문자열을 동시에 파일로 저장한다. 파일이름은 echo_history.log이며 append로 계속 추가되게 한다.
과제 순차적 채팅 프로그램 작성 앞의 과제를 수정하여 다음의 조건을 만족하는 프로그램을 작성한다. 클라이언트는 표준입력으로 받은 문자열을 서버로 전송 명령어행 인자가 아닌 프로그램 실행 중 입력 받음 서버는 받을 문자열을 출력하고 사용자로부터 표준입력으로 문자열을 받아 클라이언트로 전송 클라이언트는 받은 문자열을 출력하고 위를 반복 서버와 클라이언트는 상대방 문자열 출력시 앞에 상대방의 IP를 표기할 것, 형식은 자유 ex) From 192.168.0.1 : Hello…!!! /quit를 입력시 프로그램 종료