외로운 Nova의 작업실

c 소켓프로그래밍 공부 - 4(에코 서버) 본문

Programming/C

c 소켓프로그래밍 공부 - 4(에코 서버)

Nova_ 2022. 3. 22. 11:43

안녕하세요, 지난 3장에서는 hello worl를 서로 받아서 클라이언트가 출력하는 소켓프로그래밍을 진행했었습니다.

이번 4장에서는 좀 더 업그레이드 버전인 에코 서버를 구현해보려고했습니다.

에코 서버란 클라이언트가 서버에게 특정 문자열을 보내면 서버가 다시 그 문자열을 클라이언트에게 전송해 출력해내는 서버입니다.

예를 들어 클라이언트가 "안녕"이렇게 보내면 서버도 "안녕" 이렇게 클라이언트에게 보내지고 클라이언트는 그 문자열을 콘솔창에 출력하게됩니다.

오늘 안으로 끝내려고했지만 이상한 오류를 마주하게됬습니다.

그게 뭐냐면 클라이언트가 문자열을 보내면 서버에서 잘 받지만 클라이언트에서 출력할때 ?문자에서 儆 문자로 계속 변경 됩니다.

이에관해서 고민해봤지만 결국 답을 알 순 없었습니다.

책은 윤성우의 tcp/ip를 보고있어서 책에 적혀있는대로 코딩하면 될것같긴하지만, 저는 제생각대로 코딩하는 것을 즐겨하다보니 이런 문제가 발생한 것 같았습니다.

일단 문제를 좀 공유하려고합니다.아래는 에코 서버 소스코드입니다.

//echo server program code#include <stdio.h>#include <WinSock2.h>void ErrorHandling(char *message);

int main(int argc, char* argv[]) {

#define BUF_SIZE 1024//recv받음 메모리크기 설정

SOCKET servSocket, clntSocket;//서버소켓과 클라이언트 소켓 선언
SOCKADDR_IN servAddr, clntAddr;//서버 주소 구조체와 클라이언트 주소 구조체 선언int clntAddrLen = sizeof(clntAddr);//클라이언트 주소 구주체의 크기 변수 초기화 선언
WSADATA wsaData;// 라이브러리 버전 구조체 선언char message[BUF_SIZE];// 클라이언트로 부터 받은 메세지 받을 변수int messLen = 0;;//클라이언트로부터 메시지의 길이를 담을 변수int test = 1;

if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)//라이브러리 버전 2.2버전 사용 함수
ErrorHandling("WSAStartup() error!");

servSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

if (servSocket == INVALID_SOCKET)
ErrorHandling("socket() error!");
memset(&servAddr, 0, sizeof(servAddr));// 서버 주소 구조체 초기화
servAddr.sin_family = AF_INET;//IPv4주소체계사용
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);//현재 컴퓨터 ip로 서버 열음
servAddr.sin_port = htons(atoi(argv[1]));// 입력받은 포트로 서버 열음if (bind(servSocket, (SOCKADDR*) &servAddr, sizeof(servAddr)) == SOCKET_ERROR)//서버 소켓을 서버 주소 구조체에 담긴 주소로 연결
ErrorHandling("bind() error!");

if (listen(servSocket, 5) == SOCKET_ERROR)//서버 소켓 연결 시작
ErrorHandling("listen() error!");

for (int i = 1; i < 6; i++) {//5번만 실행

clntSocket = accept(servSocket, (SOCKADDR*)&servAddr, &clntAddrLen);//연결요청을 받고 clntSocket 생성if (clntSocket == -1)//만약 실패하면 에러 출력, 정상이라면 몇번째인지 출력
ErrorHandling("accpt() error!");
else
printf("connected cient : %d \\n", i);

while ((messLen = recv(clntSocket, message, BUF_SIZE - 1, 0)) != 0) {//클라이언트로부터 메시지를받으면 그대로 반환
message[messLen-1] = '\\0';//recv함수는 전달받은 문자열의 마지막에 null을 넣어주지않기때문에 직접 넣음printf("recv : %s\\nmessLen : %d\\n", message, messLen);
send(clntSocket, message, BUF_SIZE, 0);
}

closesocket(clntSocket);//메시지를 다 돌려줬으면 소켓 종료
}

closesocket(servSocket);
WSACleanup();
return 0;
}

void ErrorHandling(char* message) {

fputs(message, stderr);
fputc('\\n', stderr);
exit(1);
}

아래는 클라이언트 소켓코드 입니다.

//echo client socket programming#include <stdio.h>#include <WinSock2.h>#include <WS2tcpip.h>#include <string.h>#define BUF_SIZE 1024
void ErrorHandling(char message[]);
int main(int argc, char* argv[]) {

SOCKET hostSocket;//클라이언트의 소켓 생성
SOCKADDR_IN servAddr;//서버의 주소 구조체 생성
WSADATA wsaData;//라이브러리 호환 구조체 선언char message[BUF_SIZE];//콘솔창에서 메세지를 입력받아서 저장할 변수int messageLen;//메세지의길이를 담을 변수char recvMessage[BUF_SIZE] = "hello my name is su yong";//서버로부터 오는 문자열을 받을 변수if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)//소켓 버전 2.2 설정
ErrorHandling("WSAData() error!");

hostSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);// 호스트 소켓 생성if (hostSocket == INVALID_SOCKET)//에러 처리
ErrorHandling("socket() error!");

memset(&servAddr, 0, sizeof(servAddr));//통신할 서버 주소 구조체설정
servAddr.sin_family = AF_INET;//IPv4 주소 체계 사용
servAddr.sin_port = htons(atoi(argv[2]));// 2번째 인자로 포트번호 설정if (inet_pton(AF_INET, argv[1], &servAddr.sin_addr) != 1)//통신할 서버 주소 구조체의 ip주소 할당
ErrorHandling("inet_pton() error!");

if (connect(hostSocket, (SOCKADDR*) &servAddr, sizeof(servAddr)) == SOCKET_ERROR)//서버와 통신 연결 시도
ErrorHandling("connect() error!");
else
puts("connectign....");

while (1) {

fputs("input message(Q to Quit) :", stdout);//콘솔창에 q아니면 메시지입력하라고 뜨게함
fgets(message, BUF_SIZE, stdin);// 입력을 하나 받음if (!strcmp(message, "q\\n") || !strcmp(message, "Q\\n"))//만약 입력한값이 q나Q면 반복문을 끝내고 소켓을 닫음break;

send(hostSocket, message, strlen(message), 0);//콘솔창에서 받은 메시지를 서버로 전달printf("보낸 메세지 : %s메세지 길이 : %d\\n", message, (int)strlen(message));

messageLen = recv(hostSocket, recvMessage, BUF_SIZE - 1, 0);//서버로부터 메시지를 받아서 recvMessage에 담음 크기는 버퍼사이즈-1해야함, 안하면 오버플로발생가능
recvMessage[messageLen - 1] = '\\0';//recv함수는 전달받은 문자열만 넣어주고 마지막에 null을 넣지않기때문에 직접 넣어줌printf("messageLen : %d , ", messageLen);

printf("message from server : %s\\n", recvMessage);
}

closesocket(hostSocket);
WSACleanup();
return 0;
}

void ErrorHandling(char message[]) {
fputs(message, stderr);
exit(1);
}

이렇게 코드를 짜고서 직접 돌려보면 아래와 같은 상황에 마주하게됩니다.

아래는 서버 프롬프트 화면입니다

아래는 클라이언트 프롬프트 화면입니다.

위 와같이 asd를 일정하게 서버로 보내고있지만 클라이언트로 오는 서버의 문자열은 동일하지않고 계속 특정 문자로 변형됩니다.

특히나 한자는 배열을 초기화하지않았을때 들어가는 예쁜쓰레기값이라는것도 알게되었습니다.

그러나 입출력 버퍼나 문자열의 끝, 배열의 초기화, recv() send()함수의 오류같지만 계속 해결방법을 찾으려했지만 못찾았습니다.

그래서 네이버 지식인에도 올려보았습니다.

https://kin.naver.com/qna/detail.naver?d1id=1&dirId=1040101&docId=415333225#

소켓프로그래밍 문자열 에코서버 버퍼 문제 c언어 소켓프로그래밍으로 간단한 echo서버와 클라이언트 코드를 짯습니다. 근데 클라이언트쪽에서 서버로부터 답변받으면 답변받은 문자열이 계속 ?문자에서 儆 문자로 바뀌면서 문자열이... kin.naver.com

 

소켓프로그래밍 문자열 에코서버 버퍼 문제

c언어 소켓프로그래밍으로 간단한 echo서버와 클라이언트 코드를 짯습니다. 근데 클라이언트쪽에서 서버로부터 답변받으면 답변받은 문자열이 계속 ?문자에서 儆 문자로 바뀌면서 문자열이...

kin.naver.com

일단 첫번째 답변대로 코드를 변경해보았지만 상황은 같았습니다. 아니 오히려 서버쪽에서 문자열을 받지 못하는 상황도 연출 되었습니다.

오늘은 여기까지 공부했지만 내일은 이 문제를 해결하고 에코 서버를 넘어서 UDP형식으로 클라이언트와 통신해볼까합니다.

만약 위의 문제를 알고계시다면 답글로 해결방법을 알려주시면 감사하겠습니다.

Comments