반응형
    
    
    
  
                              Notice
                              
                          
                        
                          
                          
                            Recent Posts
                            
                        
                          
                          
                            Recent Comments
                            
                        
                          
                          
                            Link
                            
                        
                    | 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | 
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | 
| 23 | 24 | 25 | 26 | 27 | 28 | 29 | 
| 30 | 
                            Tags
                            
                        
                          
                          - 가천대
- Nestjs
- 자바스크립트
- socket
- 수학
- Node.js
- 타입스크립트
- 문자열
- Algorithm
- HTTP 완벽 가이드
- Crawling
- type challenge
- ip
- BFS
- 레벨 1
- 백준
- HTTP
- 크롤링
- 쉬운 문제
- TCP
- 프로그래머스 레벨 2
- typescript
- 그래프
- 프로그래머스
- javascript
- dfs
- 타입 챌린지
- dp
- 알고리즘
- 소켓
                            Archives
                            
                        
                          
                          - Today
- Total
kakasoo
[TCP/IP] iteractive 기반의 서버, 클라이언트 구현 (code) 본문
반응형
    
    
    
  클라이언트에서 입력한 것을, 서버가 받아서, 동일한 값을 되돌려 주는 에코 서버, 클라이언트이다.
자세한 내용은 모두 주석으로 처리하였으니, 읽고 기억을 되새겨보도록 하자.
echo_server.c
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #include <stdio.h> #include <stdlib.h> #include <string.h> #include <WinSock2.h> #define BUF_SIZE 1024 void ErrorHandling(char* message) {     fputs(message, stderr);     fputc('\n', stderr);     exit(1); } int main(int argc, char* argv[]) {     WSADATA wsaData;     SOCKET hServSock, hClntSock; // 호스트 서버 소켓, 호스트 클라이언트 소켓     char message[BUF_SIZE]; // 이번에는 "hello world" 가 아니라 직접 입력을 받는다.     int strLen;     SOCKADDR_IN servAdr, clntAdr; // 서버 주소를 담을 변수 두 개를 선언하였다.     int clntAdrSize = sizeof(clntAdr);     if (argc != 2) {         printf("Usage : %s <port>\n", argv[0]); // PORT 정보가 없을 시에 종료시킨다.         exit(1);     }     if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0)         ErrorHandling("WSAStartup() error!");     hServSock = socket(PF_INET, SOCK_STREAM, 0); // IPv4, 연결지향형 socket 생성     if (hServSock == INVALID_SOCKET) // 유효하지 않은 소켓이라면 (-1 값이지만 매크로 상수로 하는 것을 권장.)         ErrorHandling("Socket() error!");     /* 여기는 서버 소켓의 정보를 구성하는 부분이다 */     {         memset(&servAdr, 0, sizeof(servAdr)); // servAdr를 모두 0으로 초기화한다.         servAdr.sin_family = AF_INET;         // AF_INET과 PF_INET은 둘 다 2의 값을 가지는 상수이지만, 주소일 때는 AF, 프로토콜일 때는 PF로 나타낸다.         servAdr.sin_addr.s_addr = htonl(INADDR_ANY);         // INADDR_ANY는 서버의 IP 주소를 자동으로 할당하는 것이기 때문에, 직접 입력할 필요를 줄인다.         servAdr.sin_port = htons(atoi(argv[1])); // 입력받은 PORT 값을 서버 측 PORT로 지정한다, PORT는 데이터 송수신 창구.     }     if (bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) // 이렇게 만든 정보를 bind 한다.         ErrorHandling("bind() error!");     if (listen(hServSock, 5) == SOCKET_ERROR)         ErrorHandling("listen() error!");     for (int i = 0; i < 5; i++) {         hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &clntAdrSize); // clntAdrSize는 값을 반환받기 받는다.         if (hClntSock == -1)             ErrorHandling("accept() Error!");         else             printf("Connected client %d \n", i + 1);         while ((strLen = recv(hClntSock, message, BUF_SIZE, 0)) != 0)              // 수신대상 hClntSock에 메세지, 수신 가능한 길이를 매개변수로 준다.             // 성공 시 strLen은 수신한 바이트 수가 저장된다. // echo 하기 위해서 만들어진 반복문.             send(hClntSock, message, strLen, 0);             // while() 내부의 send() 함수는 hClntSock으로 message의 strLen만큼의 길이를 전달하고, 전송한다.         closesocket(hClntSock); // 전송 후에 종료한다.     }     closesocket(hServSock); // 모든 클라이언트의 전송이 종료되면 서버 소켓도 종료시킨다.     WSACleanup(); // 소켓 관련 함수들을 반환     return 0; } | cs | 
echo_client.c
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | #define _WINSOCK_DEPRECATED_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <WinSock2.h> #define BUF_SIZE 1024 void ErrorHandling(char* message) {     fputs(message, stderr);     fputc('\n', stderr);     exit(1); } int main(int argc, char* argv[]) {     WSADATA wsaData;     SOCKET hSocket; // 호스트 클라이언트 소켓     char message[BUF_SIZE]; // 이번에는 "hello world" 가 아니라 직접 입력을 받는다.     int strLen;     SOCKADDR_IN servAdr; // 서버 주소를 담을 변수를 선언하였다 (클라이언트 소켓의 주소는 필요없다.)     if (argc != 3) {         printf("Usage : %s <IP> <port>\n", argv[0]); // IP나 PORT 정보가 없을 시에 종료시킨다.         exit(1);         // 여담인데, connect() 함수는 자동으로 IP와 PORT를 할당하기 때문에 클라이언트는 bind()가 필요없다.         // 명시적으로 할 필요가 없다는 것이지, 쓰임이 없다는 것은 아니다.     }     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)         ErrorHandling("WSAStartup() error!");     hSocket = socket(PF_INET, SOCK_STREAM, 0); // IPv4, 연결지향형 socket 생성     if (hSocket == INVALID_SOCKET) // 유효하지 않은 소켓이라면 (-1 값이지만 매크로 상수로 하는 것을 권장.)         ErrorHandling("Socket() error!");     /* 여기는 서버 소켓의 정보를 구성하는 부분이다 */     {         memset(&servAdr, 0, sizeof(servAdr)); // servAdr를 모두 0으로 초기화한다.         servAdr.sin_family = AF_INET;         // AF_INET과 PF_INET은 둘 다 2의 값을 가지는 상수이지만, 주소일 때는 AF, 프로토콜일 때는 PF로 나타낸다.         servAdr.sin_addr.s_addr = inet_addr(argv[1]); // 서버와 클라이언트의 IP가 같은 경우는 거의 없을 것.         // INADDR_ANY는 서버의 IP 주소를 자동으로 할당하는 것이기 때문에, 직접 입력할 필요를 줄인다.         servAdr.sin_port = htons(atoi(argv[2]));         // 입력받은 PORT 값을 서버 측 PORT로 지정한다, PORT는 데이터 송수신 창구.         // 서버 측과 바뀐 것은, argv[1]이 argv[2]로 변경되었다는 것 하나 뿐이다.     } //    if (bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) // 이렇게 만든 정보를 bind() 한다. //        ErrorHandling("bind() error!"); //  위에서 말했다시피, bind()는 클라이언트 측에는 필요가 없다, connect() 할 때 자동생성되기 때문이다.     if (connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)         ErrorHandling("connect() error!");     else         puts("Connected!");     while (1) {         fputs("Input message (Q to quit) : ", stdout); // stderr는 디버깅, stdout은 출력과 관련된 표준 스트림이다.         fgets(message, BUF_SIZE, stdin); // 입력 받은 data를 message에 저장.         if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))             break; // q or Q를 입력한 경우에는 전송 종료.         send(hSocket, message, strlen(message), 0); // 그 외인 경우에는 서버 측으로 전송         strLen = recv(hSocket, message, BUF_SIZE - 1, 0); // 서버 측으로부터 에코된 게 있는지 확인 (문자열 길이를 반환)         message[strLen] = 0; // 마지막 값은 0으로 지정 (문자열의 마지막)         printf("message from server : %s", message); // 정상적이라면 message에 client에서 입력한 것이 동일하게 돌아와야 한다.     }     closesocket(hSocket); // 모든 클라이언트의 전송이 종료되면 서버 소켓도 종료시킨다.     WSACleanup(); // 소켓 관련 함수들을 반환     return 0; } | cs | 
이 코드에는 문제점이 있다.
]client에서 받아들이는 문자열의 크기가 1024를 초과하는 경우, 즉 BUF_SIZE를 초과하는 경우이다.
초과할 경우에는 운영체제가 알아서 정보를 잘게 잘라서 전송해주겠지만, 이는 운영체제의 이야기일 뿐이고,
코드에서는 while 문 내에서, 이미 정보가 돌아왔음으로 판단하고 재전송하는 단계로 넘어갈 수가 있다.
다음에는 이를 해결해보자.
여담이지만, Q를 누르면 코드가 종료되는 것은, 어플리케이션 프로토콜에 해당한다.
반응형
    
    
    
  '프로그래밍 > 네트워크' 카테고리의 다른 글
| [TCP/IP] TCP 기반의 Half-close (0) | 2020.07.17 | 
|---|---|
| [TCP/IP] 클라이언트, BUF_SIZE 초과 해결, Buffer에 대하여 (0) | 2020.07.17 | 
| [TCP/IP] iteractive 기반의 서버, 클라이언트 구현 (0) | 2020.07.17 | 
| [TCP/IP] TCP 기반 서버, 클라이언트 구현 (0) | 2020.07.17 | 
| [TCP/IP] TCP/IP 프로토콜 스택 (2) (0) | 2020.07.15 |