반응형
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 | 31 |
Tags
- 프로그래머스
- 타입 챌린지
- 소켓
- 프로그래머스 레벨 2
- 자바스크립트
- 타입스크립트
- Nestjs
- Node.js
- Crawling
- 가천대
- typescript
- 백준
- socket
- 그래프
- BFS
- 크롤링
- dp
- Algorithm
- TCP
- 레벨 1
- 쉬운 문제
- 문자열
- dfs
- type challenge
- ip
- 수학
- HTTP
- 알고리즘
- javascript
- HTTP 완벽 가이드
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 |