소켓 프로그래밍 기초 질문입니다.

seongwon1의 이미지

안녕하세요?

간단한 iterative 소켓 서버와 클라이언트를 만들었는데요. 조금 이상한

점이 있어서 질문을 드립니다.

일단 서버 코드는요.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define MAX_BUF 1024

int main(int argc, char *argv[]){
	
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	int sock_server;
	int sock_client;
	char message[MAX_BUF];
	char message1[]="HI!!";
	int clnt_addr_size;
	int size;


	if(argc != 2){
		fprintf(stderr, "Usage : %s <port>\n", argv[0]);
		exit(1);
	}	
	///////// make socket ////////////////
	if((sock_server = socket(AF_INET, SOCK_STREAM, 0))== -1){
		fprintf(stderr, "Fail to make socket descriptor\n");
		exit(1);
	}
	
	//////// initialize server_addr ////////
	memset(message, 0, sizeof(message));
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(argv[1]));
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	//////// bind with server addr /////////////
	if(bind(sock_server, (struct sockaddr *)&server_addr, sizeof(server_addr))== -1){
		fprintf(stderr, "Fail to bind with socket descriptor\n");
		exit(1);
	}
	
	////////// server ready to accept
	if(listen(sock_server, 5)==-1){
		fprintf(stderr, "Fail to listen\n");
		exit(1);
	}
	clnt_addr_size=sizeof(client_addr);
	
	while(1){
		
		if((sock_client=accept(sock_server, (struct sockaddr *)&client_addr, &clnt_addr_size))==-1){
			fprintf(stderr, "Fail to accept for client\n");
			exit(1);
		}
		
		read(sock_client, message, sizeof(message));
		message[strlen(message)] = '\0';
		
		if(strcmp(message, "end")==0)
			break;
		
		printf("client input %s\n", message);
		
		write(sock_client, message1, sizeof(message1));
		
		close(sock_client);
	}
	write(sock_client, "Bye!!", 5);
	close(sock_client);
	printf("All client had gone!!\n");
	close(sock_server);
	return 0;
}

이구요. 클라이언트 코드는

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define MAX_BUF 1024

int main(int argc, char *argv[]){
	
	struct sockaddr_in client_addr;
	int sock_client;
	char message[MAX_BUF];
	char message1[MAX_BUF];
	int size;


	if(argc != 3){
		fprintf(stderr, "Usage : %s <ip address>, <port>\n", argv[0]);
		exit(1);
	}	
	///////// make socket ////////////////
	if((sock_client = socket(AF_INET, SOCK_STREAM, 0))== -1){
		fprintf(stderr, "Fail to make socket descriptor\n");
		exit(1);
	}
	
	//////// initialize server_addr ////////
	memset(&client_addr, 0, sizeof(client_addr));
	client_addr.sin_family = AF_INET;
	client_addr.sin_port = htons(atoi(argv[2]));
	client_addr.sin_addr.s_addr = inet_addr(argv[1]);
	
	printf("Enter the string to input server : ");
	gets(message1);	
	
	if(connect(sock_client, (struct sockaddr *)&client_addr, sizeof(client_addr))== -1){
		fprintf(stderr, "Fail connect to server\n");
		exit(1);
	}
		
	write(sock_client, message1, sizeof(message1));
	read(sock_client, message, sizeof(message));
	message[strlen(message)] = '\0';

	printf("Server said %s\n", message);
		
	close(sock_client);
	return 0;
}

위와 같습니다.

서버가 argument로 포트 번호를 받아서 구동을 시작하면, 클라이언트가

argument로 ip와 port 번호를 받아서 접속을 하고

특정 문자열을 입력하면 서버는 그냥 "HI!!"라고 결과를 리턴해주는

왕초보 유치 서버인데요. :oops: 기본적인 작동은 매우 정상적으로

잘 됩니다만, 클라이언트에서 "end"를 입력했을 경우, 즉 코드를 보시면

아시겠지만 "end" 입력시 서버가 종료되게 되는데요.

이렇게 해서 서버를 종료하고 나면 다시 서버를 실행했을 경우

"Fail to bind with socket descriptor" 라는 에러를 내면서 서버가

시작이 안됩니다. 아시다시피 bind()에서 실패를 했다는 뜻인데요.

왜 실패를 하는 건지 잘 이해가 안됩니다. 그래서 이상하다..

다시 컴파일 하고 해볼까? 하고 컴파일하고 다시 실행했더니 다시

실행이 되네요. -_-;;

뭐가 문제인건지....고수님들의 도움 부탁드립니다.

그럼 모두모두 좋은 하루 보내시구요.

수고하시길...

leilei의 이미지

setsockopt()에 SO_REUSEADDR 을 사용해 보세요..
비슷한 내용의 글이 많답니다.. 검색 한번 해 보시길..

dudungsil의 이미지

TIME_WAIT에 대해서 찾아 보세요.

산넘어 산

seongwon1의 이미지

컴파일을 다시 하면 되거나 하는게 아니라 일정 시간이 지나면 다시

되는 거였습니다.

IPC의 FIFO를 배우면서 파이프가 일정시간동안은 사라지지 않는다는

내용을 읽은 것 같은데 이것도 그거랑 비슷한지도 모르겠네요.

조금 더 찾아봐야 할 듯.

답변 감사드립니다.

sorcerer의 이미지

참고로 에러 처리할 때는 perror()를 사용하십시요.
그러면 정확한 에러메시지를 확인 할 수 있습니다.
예를 들면,

if((sock_client = socket(AF_INET, SOCK_STREAM, 0))== -1){
      fprintf(stderr, "Fail to make socket descriptor\n");
      exit(1);
   } 

이 부분을

if((sock_client = socket(AF_INET, SOCK_STREAM, 0))== -1){
      perror( "socket()" );
      exit(1);
   }

라고 하시면 정확한 에러의 이유가 나올 것입니다.
perror()의 인자는 아무거나 어디서 에러가 발생 했는지 알 수 있는 문자열이면
됩니다. 위의 예에선 socket() 이 어떤 에러를 내는지 알기 위함이므로 인자로
socket()을 넘긴 것입니다. 뭐 "소켓에서에러" 라고 한글로 해도 되겠지요.

SOrCErEr

hekimian의 이미지

dudungsil 님이 말씀하신대로 TIME_WAIT 상태 때문에 그렇습니다.

이 상태는 TCP에서 먼저 close를 호출한 쪽에서 거치는 상태입니다.

TCP에서 이러한 상태를 두는 이유는 (스티븐 아저씨 책에 있는 내용)

1. 신뢰성 있는 TCP의 양방향 연결종료 보장
2. 네트워크에서 오래된 복사본 세그먼트를 소멸하도록 한다.

입니다.

쉽게 이해 하시려면 TCP 상태전이와 TCP 연결종료에 대해서 보시면 될 겁니다.

나를 죽이지 않는 모든 것은 나를 강하게 할 뿐이다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.