에코 서버란 클라이언트에서 수신 된 데이터를 그대로 다시 클라이언트에게 되돌려 보내는 서버를 의미한다.
Iterative란 반복적이라는 의미로 각 클라이언트의 연결 요청을 순차적으로 처리하는 서버를 의미한다.
이 서버는 한 번에 하나의 클라이언트 요청만 처리하며, 하나의 연결이 완료된 후 다음 연결을 처리한다.
// Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define BUF_SIZE 1024
void ErrorHandling(char* message);
int main(int argc, char* argv[]) {
WSADATA wsaData;
SOCKET hServSock, hClntSock;
char message[BUF_SIZE];
int strLen, i;
SOCKADDR_IN servAddr, clntAddr;
int clntAddrSize;
if (argc != 2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
ErrorHandling("WSAStartup() error!");
}
hServSock = socket(PF_INET, SOCK_STREAM, 0);
if (hServSock == INVALID_SOCKET) {
ErrorHandling("socket() error!");
}
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(atoi(argv[1]));
if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) {
ErrorHandling("bind() error!");
}
if (listen(hServSock, 5) == SOCKET_ERROR) {
ErrorHandling("listen() error!");
}
clntAddrSize = sizeof(clntAddr);
// 연결 요청 대기 큐의 크기만큼 반복
for (i = 0; i < 5; i++) {
hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &clntAddrSize);
if (hClntSock == -1) {
ErrorHandling("accept() error!");
}
// 연결 되었다면 몇 번째 클라이언트인지 출력
else {
printf("Connected client %d \n", i + 1);
}
// 클라이언트한테 데이터를 받으면
while ((strLen = recv(hClntSock, message, BUF_SIZE, 0)) != 0) {
// 그대로 돌려주기
send(hClntSock, message, strLen, 0);
}
closesocket(hClntSock);
}
closesocket(hServSock);
WSACleanup();
return 0;
}
void ErrorHandling(char* message) {
fputs(message, stderr);
fputs("\n", stderr);
exit(1);
}
// Client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define BUF_SIZE 1024
#define _WINSOCK_DEPRECATED_NO_WARNINGS
void ErrorHandling(char* message);
int main(int argc, char* argv[])
{
WSADATA wsaData;
SOCKET hSocket;
char message[BUF_SIZE];
int strLen;
SOCKADDR_IN servAddr;
if (argc != 3) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
ErrorHandling("WSAStartup() error!");
}
hSocket = socket(PF_INET, SOCK_STREAM, 0);
if (hSocket == INVALID_SOCKET) {
ErrorHandling("socket() error!");
}
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(argv[1]);
servAddr.sin_port = htons(atoi(argv[2]));
if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) {
ErrorHandling("connect() error!");
}
else {
printf("Connected.........");
}
while (1) {
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
// 입력 값이 q or Q이면 종료
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) {
break;
}
// 입력 값 보내기
send(hSocket, message, strlen(message), 0);
strLen = recv(hSocket, message, BUF_SIZE - 1, 0);
message[strLen] = 0;
// 서버로부터 받은 값 출력
printf("Message from server: %s", message);
}
closesocket(hSocket);
WSACleanup();
return 0;
}
void ErrorHandling(char* message) {
fputs(message, stderr);
fputs("\n", stderr);
exit(1);
}
2개의 클라이언트를 실행하였다.
2개의 클라이언트를 실행 했으나 서버에는 Connected client1만 뜬 상태에서 client1과 1대1 통신이 진행되었다.
client1에서 입력한 값을 서버에서 그대로 돌려주었고 q를 눌러 연결을 끊었을 때 서버에서 Connected clinet2가 뜨며 client2와 연결이 되었다.
멈춰있던 client2에서도 입력이 가능해졌고 입력값을 그대로 돌려받았다.
위의 Iterative 에코 클라이언트에서 문제가 생길 수 있는 부분이 있다.
while (1) {
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) {
break;
}
send(hSocket, message, strlen(message), 0);
strLen = recv(hSocket, message, BUF_SIZE - 1, 0);
message[strLen] = 0;
printf("Message from server: %s", message);
}
해당 부분에서 입력 받은 데이터를 send를 통해 통째로 서버에 보내고 있다.
다음줄에서는 recv를 통해 서버에서 데이터를 통째로 받고 있다.
서버에서 데이터를 다 전달하지 못하였는데 recv를 해버리는 경우 문제가 발생할 수 있다.
while (1) {
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) {
break;
}
strLen = send(hSocket, message, strlen(message), 0);
recvLen = 0;
while (recvLen < strLen) {
recvCnt = recv(hSocket, message, BUF_SIZE - 1, 0);
if (recvCnt == -1) {
ErrorHandling("recv() error!");
}
recvLen += recvCnt;
}
message[strLen] = 0;
printf("Message from server: %s", message);
}
위와 같이 바꾸어 주면 해당 문제를 해결할 수 있다.
strLen에 send 해주는 message의 길이를 저장해 둔 후 recv한 데이터의 길이가 strLen보다 커질 때 while을 빠져나와 출력을 해주면 된다.
'네트워크' 카테고리의 다른 글
소켓 프로그래밍 - TCP 소켓 (0) | 2023.09.10 |
---|---|
ReaderWriterLock 구현 (0) | 2023.08.05 |
ReaderWriterLock (0) | 2023.08.04 |
AutoResetEvent (0) | 2023.08.03 |
SpinLock (0) | 2023.08.02 |