TCP/IP 소켓프로그래밍 공부 중 질문이 있습니다. 클라이언트가 서버없이도 작동하는데 어떻게 된 일이죠?

qeeeeep의 이미지

안녕하세요 TCP/IP 소켓 프로그래밍을 공부하는 학생입니다.

이제 막 서버소켓과 클라이언트소켓을 TCP 형식으로 만들어 연결하는 것을 공부하였습니다.

연습 예제로 윈도우즈10 콘솔환경에서 클라이언트가 사용자에게 문자열을 입력받아 서버로 보낸 뒤

서버는 그 문자열을 다시 클라이언트로 보내고 클라이언트가 그것을 출력하는 에코 서버-클라이언트 소켓을

만들고 있었습니다.

그런데 이렇게 만든 예제를 실행할 때 클라이언트의 에러핸들링함수를 만들지 않으면

서버를 실행하지 않아도 클라이언트가 정상작동되는 현상을 발견하였습니다.

연결한 소켓으로부터 데이터를 수신하는 recv()함수가 작동하는 것입니다.

이것이 정상적인 상황인건가요? 왜 이런 현상이 발생하는지 궁금합니다.

첨부파일은 실행 결과 스크린샷 입니다.

아래는 서버소스입니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
 
#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 servAdr, clntAdr;
	int clntAdrSize;
 
	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(&servAdr, 0, sizeof(servAdr));
	servAdr.sin_family=AF_INET;
	servAdr.sin_addr.s_addr=htonl(INADDR_ANY);
	servAdr.sin_port=htons(atoi(argv[1]));
 
	if(bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)
		ErrorHandling("bind() error");
 
	if(listen(hServSock, 5)==SOCKET_ERROR)
		ErrorHandling("listen() error");
 
	clntAdrSize=sizeof(clntAdr);
 
	for(i=0;i<5;i++)
	{
		hClntSock=accept(hServSock, (SOCKADDR*)&clntAdr, &clntAdrSize);
		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)
{}

다음은 클라이언트 소스입니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
 
#define BUF_SIZE 1024
void ErrorHandling(char*);
 
int main(int argc, char *argv[])
{
	WSADATA wsaData;
	SOCKET hSocket;
	char message[BUF_SIZE];
	int strLen;
	SOCKADDR_IN servAdr;
 
	if(argc!=3) {
		printf("Usage: %s <IP> <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(&servAdr, 0, sizeof(servAdr));
	servAdr.sin_family=AF_INET;
	servAdr.sin_addr.s_addr=inet_addr(argv[1]);
	servAdr.sin_port=htons(atoi(argv[2]));
 
	if(connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr))==SOCKET_ERROR)
		ErrorHandling("connect() error");
	else
		puts("connected.......");
 
	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);
	}
	closesocket(hSocket);
	WSACleanup();
	return 0;
}
 
void ErrorHandling(char * message)
{}
File attachments: 
첨부파일 크기
Image icon echo_ghost.png10.09 KB
akayong의 이미지


while(1) 안에서...
message 버퍼에다가 입력 받은 후에.

recv 함수 호출 후에 strLen 값 한번 찍어 보세요....

그 바로 밑에서
message값 그거 그대로 찍고 있으니까요.... 똑같이 나오는거 같은데..
지금 컴파일 할 수 없는 환경이라 제가 테스트를 못하겠네요.

qeeeeep의 이미지

send하려고 입력한 배열메모리를 다시 출력하고 있었기 때문이었네요!! 감사합니다!!

ymir의 이미지

connect 가 정상적으로 수행되었다면 화면에 "connected....." 가 출력되었겠죠?
connect() 가 이미 실패했기 때문에, 거기서 종료했어야 하는데.. 그냥 계속 진행하셨네요.
그 이후의 내용은 그냥 수행되서는 안 되는 코드가 수행된 것일 뿐 아무런 의미가 없습니다.

거기에 socket 이 connect 되지도 않았는데 recv() 를 했으니, strLen 에는 -1 이 리턴되었을 텐데..
어쨌든 recv() 가 에러를 리턴한 상황에서도 그걸 무시하고, 위험하게 message[-1] 에 0 을 집어넣었을 뿐만 아니라..
그냥 message 를 출력해 버렸으니.. 처음에 입력받은 message 의 내용이 고스란히 출력될 수 밖에 없겠죠.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

qeeeeep의 이미지

그렇네요.. 보낼때 입력한 메시지와 같은 메모리를 다시 출력했었기 때문이네요..;; 감사합니다!!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.