^(코딩캣)^ = @"코딩"하는 고양이;
썸네일 이미지
[BSD Socket] FreeBSD(+*nix) 버전 UDP 클라이언트측 코드
BSD Socket 사용법 정리 FreeBSD(+*nix) 버전 클라이언트측 코드 본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 UDP Socket 클라이언트측 코드의 사용 예를 정리한다. 전체 코드 먼저 전체적인 코드를 훑어본다. /* 표준 헤더 include */ #include // intptr_t #include // exit() #include // printf(), sprintf() #include // assert() #include // strerror() #include // errno /* 유닉스 헤더 include */ #include // pthread_create(), pthread_exit(), pthread_cleanup_push(), pthread_clean..
BSD Socket
2020. 3. 15. 08:06

[BSD Socket] FreeBSD(+*nix) 버전 UDP 클라이언트측 코드

BSD Socket
2020. 3. 15. 08:06

BSD Socket 사용법 정리


FreeBSD(+*nix) 버전 클라이언트측 코드


본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 UDP Socket 클라이언트측 코드의 사용 예를 정리한다.

 

전체 코드


먼저 전체적인 코드를 훑어본다.

/* 표준 헤더 include */
#include <stdint.h> // intptr_t
#include <stdlib.h> // exit()
#include <stdio.h> // printf(), sprintf()
#include <assert.h> // assert()
#include <string.h> // strerror()
#include <errno.h> // errno

/* 유닉스 헤더 include */
#include <pthread.h> // pthread_create(), pthread_exit(), pthread_cleanup_push(), pthread_cleanup_pop()
#include <strings.h> // bzero()
#include <unistd.h> // ssize_t, read(), write()

/* 소켓 헤더 include */
#include <arpa/inet.h> // htons()
#include <netinet/in.h> // struct sockaddr_in
#include <sys/types.h> // socket
#include <sys/socket.h> // socket

#define SCK_ADDR "192.168.204.143"
#define SCK_PORT 9998
#define SCK_BUFF 512
#define SCK_MESG "Hello, UDP Server!"

void cleanup_routine(void * pfdclient); // 소켓 함수 실행 중 오류가 발생할 때 수행할 공통의 해제 작업이 기술된 함수이다.

int main(void) {
	pthread_t thself; // 현재 실행중인 스레드 번호를 보관한다.

	int fdserver; // 서버에 연결하는 소켓에 대한 파일 디스크럽터.
	socklen_t cbaddrserver; // 서버측 인터넷 주소의 길이를 보관한다.
	struct sockaddr_in addrserver; // 서버측 인터넷 주소를 보관한다.
	int sockresult; // 소켓 함수의 실행 결과를 보관한다.

	size_t cbbuffserver, cbbuffclient; // 서버에서 클라이언트로, 클라이언트에서 서버로 송수신하는 문자열의 길이를 보관한다.
	char buffserver[SCK_BUFF], buffclient[SCK_BUFF]; // 서버에서 클라이언트로, 클라이언트에서 서버로 송수신하는 문자열을 보관한다.
	ssize_t rwresult; // 서버와 클라이언트간 송수신된 문자열의 길이를 보관한다.
	
	thself = pthread_self(); // 현재 실행중인 스레드 번호를 가져온다.
	pthread_cleanup_push(cleanup_routine, &fdserver); // 소켓 함수의 호출이 실패하면 cleanup_routine(fdserver)를 실행한다.

	/* 서버에 접속하기 위한 소켓을 생성한다. */
	fdserver = socket(AF_INET, SOCK_DGRAM, 0);
	if (fdserver < 0) {
    	// 소켓 생성에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] socket(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] socket() @ pthread_t = %p\n", thself);

	/* 접속하고자 하는 서버의 인터넷 주소를 구성한다. */
	cbaddrserver = sizeof(struct sockaddr_in); // 서버 측 인터넷 주소의 길이를 구한다.
	bzero(&addrserver, cbaddrserver); // sockaddr_in 구조체의 내용을 0으로 리셋한다.
    addrserver.sin_family = AF_INET; // 구조체 내용 구성 - 1. 주소가 인터넷 주소이다.
    addrserver.sin_addr.s_addr = inet_addr(SCK_ADDR); // 구조체 내용 구성 - 2. 서버 자신은 모든 주소로부터 연결에 응할 수 있다.
    addrserver.sin_port = htons(SCK_PORT); // 구조체 내용 구성 - 3. 서버는 SCK_PORT 매크로상수에 지정된 포트 번호로 통신한다.
	
	cbclient = SCK_BUFF * sizeof(char);
	bzero(buffclient, cbclient);
	sprintf(buffclient, "%s @ pthread_t = %p", SCK_MESG, thself);

	/* 서버에 문자열을 송신한다. */
	cbclient = strlen(buffclient) * sizeof(char);
	rwresult = sendto(fdserver, buffclient, cbclient, 0, (const struct sockaddr *)&addrserver, cbaddrserver);
	if (rwresult < 0) {
    	// 송신에 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] sendto(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] sendto(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
	printf("<< %s\n", buffclient);

	cbserver = SCK_BUFF * sizeof(char);
	bzero(buffserver, cbserver);

	/* 서버로부터 문자열을 수신한다. */
	rwresult = recvfrom(fdserver, buffserver, cbserver, 0, (struct sockaddr *)&addrserver, &cbaddrserver);
	cbserver = strlen(buffserver) * sizeof(char);
	if (rwresult < 0) {
    	// 수신에 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] recvfrom(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] recvfrom(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
	printf("<< %s\n", buffserver);

	pthread_exit((void *)((intptr_t)errno)); // 현재 errno를 리턴하며 본 스레드 종료

	pthread_cleanup_pop(0); // pthread_cleanup_push를 실행했으면 이것을 실행하며 짝을 맞춘다.
	
	return 0;
}

/*
 * 소켓 함수에 오류가 발생했을 경우 수행할 공통의 정리 작업이다.
 * pfdsocket : 오류가 발생하여 닫아야 할 소켓이다.
 */
void cleanup_routine(void * pfdsocket) {
	pthread_t thself;

	int fdsocket;
	int sockresult;

	assert(pfdserver != NULL);
	fdsocket = *((int *)pfdsocket); // 지정된 주소에서 서버 또는 클라이언트 소켓 파일 디스크럽터를 복사해 온다.
	thself = pthread_self(); // 현재 실행중인 스레드 번호를 가져온다.

	if (fdsocket >= 0) {
    	/* 소켓을 닫는다. */
		sockresult = close(fdsocket);
		if (sockresult < 0) {
			printf("[FAILURE] close(): %d(%s)\n", errno, strerror(errno)); // 소켓 닫기에 오류가 발생하면, errno를 출력한다.
		} else {
			printf("[SUCCESS] close()\n");
        }
	}

	return;
}

 

위 소스 코드를 FreeBSD에서는 다음과 같이 컴파일한 후 실행할 수 있다.

$ cc 소스파일.c -o출력파일 -lpthread

pthread 함수를 사용하기 위한 옵션으로 특별히 -lpthread가 추가되었다. 이 옵션이 없다면, pthread 함수를 호출하는 부분에서 '정의되지 않은 식별자' 오류를 낼 것이다.

서버 접속에 성공하면 다음과 같이 서버로부터 메시지를 수신한다. 여기서 0x800681500은 이 클라이언트와 통신하고 있는 서버 측 thread ID이다.

서버와 문자열 교환에 성공하였을 때 서버측에서 보낸 메시지가 출력된다.

카테고리 “BSD Socket”
more...
썸네일 이미지
[BSD Socket] FreeBSD(+*nix) 버전 UDP 서버측 코드
BSD Socket 사용법 정리 FreeBSD(+*nix) 버전 서버측 코드 본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 UDP Socket 서버측 코드의 사용 예를 정리한다. 전체 코드 먼저 전체적인 코드를 훑어본다. /* 표준 헤더 include */ #include // intptr_t #include // exit() #include // printf(), sprintf() #include // assert() #include // strerror() #include // errno /* 유닉스 헤더 include */ #include // pthread_create(), pthread_exit(), pthread_cleanup_push(), pthread_cleanup_pop..
BSD Socket
2020. 3. 14. 21:21

[BSD Socket] FreeBSD(+*nix) 버전 UDP 서버측 코드

BSD Socket
2020. 3. 14. 21:21

BSD Socket 사용법 정리


FreeBSD(+*nix) 버전 서버측 코드


본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 UDP Socket 서버측 코드의 사용 예를 정리한다.

 

전체 코드


먼저 전체적인 코드를 훑어본다.

/* 표준 헤더 include */
#include <stdint.h> // intptr_t
#include <stdlib.h> // exit()
#include <stdio.h> // printf(), sprintf()
#include <assert.h> // assert()
#include <string.h> // strerror()
#include <errno.h> // errno

/* 유닉스 헤더 include */
#include <pthread.h> // pthread_create(), pthread_exit(), pthread_cleanup_push(), pthread_cleanup_pop()
#include <strings.h> // bzero()
#include <unistd.h> // ssize_t, read(), write()

/* 소켓 헤더 include */
#include <arpa/inet.h> // htons()
#include <netinet/in.h> // struct sockaddr_in
#include <sys/types.h> // socket
#include <sys/socket.h> // socket

#define SCK_PORT 9998
#define SCK_BUFF 512
#define SCK_MESG "Hello, UDP Client!"

void cleanup_routine(void * pfdsocket); // 소켓 함수 실행 중 오류가 발생할 때 수행할 공통의 해제 작업이 기술된 함수이다.

int main(void) {
	pthread_t thself; // 현재 실행중인 스레드 번호를 보관한다.

	int fdserver; // 서버 소켓 및 클라이언트 소켓에 대한 파일 디스크럽터이다.
	socklen_t cbaddrserver, cbaddrclient; // 서버 및 클라이언트에 대한 인터넷 주소의 길이를 보관한다.
	struct sockaddr_in addrserver, addrclient; // 서버 및 클라이언트에 대한 인터넷 주소를 보관한다.
	int sockresult; // 소켓 함수의 실행 결과를 보관한다.
	
	thself = pthread_self(); // 현재 실행중인 자기 자신의 스레드 번호를 가져온다.
	pthread_cleanup_push(cleanup_routine, &fdserver); // 소켓 함수 실행 중 오류가 발생하였을 때 cleanup_routine을 실행한다.

    /* 서버 측 소켓을 생성한다. */
	fdserver = socket(AF_INET, SOCK_DGRAM, 0);
	if (fdserver < 0) {
    	// 서버 측 소켓 생성에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] socket(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] socket() @ pthread_t = %p\n", thself);

	/* 서버측에서 연결하고자 하는 대상 컴퓨터의 인터넷 주소를 구성한다. */
	cbaddrserver = sizeof(struct sockaddr_in); // 인터넷 주소의 길이를 구한다.
	bzero(&addrserver, cbaddrserver); // sockaddr_in 구조체의 내용을 0으로 리셋한다.
    addrserver.sin_family = AF_INET; // 구조체 내용 구성 - 1. 주소가 인터넷 주소이다.
    addrserver.sin_addr.s_addr = htonl(INADDR_ANY); // 구조체 내용 구성 - 2. 서버 자신은 모든 주소로부터 연결에 응할 수 있다.
    addrserver.sin_port = htons(SCK_PORT); // 구조체 내용 구성 - 3. 서버는 SCK_PORT 매크로상수에 지정된 포트 번호로 통신한다.

	/* 앞서 구성한 구조체로 서버용 소켓에 바인딩한다. */
	sockresult = bind(fdserver, (struct sockaddr *)&addrserver, cbaddrserver);
	if (sockresult < 0) {
    	// 인터넷 주소 바인딩에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] bind(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] bind() @ pthread_t = %p\n", thself);

	while (1) {
		size_t cbserver, cbclient;
		char buffserver[SCK_BUFF], buffclient[SCK_BUFF];
		ssize_t rwresult;

		printf("SERVER WAITING...\n");

		cbclient = SCK_BUFF * sizeof(char);
		bzero(buffclient, cbclient);

		cbaddrclient = sizeof(struct sockaddr_in);
		bzero(&addrclient, cbaddrclient);

		/* 클라이언트 측으로부터 문자열이 수신될 때까지 무한정 기다린다. */
		rwresult = recvfrom(fdserver, buffclient, cbclient, 0, (struct sockaddr *)&addrclient, &cbaddrclient);
		cbclient = strlen(buffclient) * sizeof(char);
		if (rwresult < 0) {
        	// 문자열 수신에서 오류가 발생하면, errno를 출력한다.
			printf("[FAILURE] recvfrom(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
            // 연결 대기 다시 한다(무한루프).
			continue;
		}
		printf("[SUCCESS] recvfrom(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
		printf(">> %s\n", buffclient);

		/* 서버에서 생성한 임의의 정수를 섞어서 클라이언트로 보낼 문자열을 구성한다. */
		srand((unsigned int)time(NULL));

		cbserver = SCK_BUFF * sizeof(char);
		bzero(buffserver, cbserver);
		sprintf(buffserver, "%s (%d) @ pthread_t = %p", SCK_MESG, rand(), thself);

		/* 클라이언트로 문자열을 송신한다. */
		cbserver = strlen(buffserver) * sizeof(char);
		rwresult = sendto(fdserver, buffserver, cbserver, 0, (const struct sockaddr *)&addrclient, cbaddrclient);
		if (rwresult < 0) {
        	// 문자열 송신에서 오류가 발생하면, errno를 출력한다.
			printf("[FAILURE] sendto(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
            // 연결 대기 다시 한다(무한루프).
			continue;
		}
		printf("[SUCCESS] sendto(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
		printf("<< %s\n", buffserver);
	}

	sockresult = close(fdserver);
	if (sockresult < 0) {
		printf("[FAILURE] close(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] close() @ pthread_t = %p\n", thself);

	pthread_exit((void *)((intptr_t)errno)); // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
	pthread_cleanup_pop(0); // pthread_cleanup_push를 실행했으면 이것을 실행하며 짝을 맞춘다.

	return 0;
}

void cleanup_routine(void * pfdsocket) {
	pthread_t thself;

	int fdsocket;
	int sockresult;

	assert(pfdsocket != NULL);
	fdsocket = *((int *)pfdsocket); // 지정된 주소에서 서버 또는 클라이언트 소켓 파일 디스크럽터를 복사해 온다.
	thself = pthread_self(); // 현재 실행중인 스레드 번호를 가져온다.

	if (fdsocket >= 0) {
    	/* 소켓을 닫는다. */
		sockresult = close(fdsocket);
		if (sockresult < 0) {
			// 소켓 닫기에서 오류가 발생하면, errno를 출력한다.
			printf("[FAILURE] close(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
		} else {
			printf("[SUCCESS] close() @ pthread_t = %p\n", thself);
        }
	}

	return;
}

 

실행 결과 보기


위 소스 코드를 FreeBSD에서는 다음과 같이 컴파일한 후 실행할 수 있다.

$ cc 소스파일.c -o출력파일 -lpthread

pthread 함수를 사용하기 위한 옵션으로 특별히 -lpthread가 추가되었다. 이 옵션이 없다면, pthread 함수를 호출하는 부분에서 '정의되지 않은 식별자' 오류를 낼 것이다.

클라이언트로부터 연결을 기다리고 있는 UDP 서버 프로그램.

 

클라이언트 접속이 확인될때마다 임의의 정수를 생성하고, 메시지에 이를 섞어서 클라이언트에게 보낸다.

클라이언트로부터 접속이 확인될 때마다 임의의 정수를 섞은 문자열을 전송한다.

카테고리 “BSD Socket”
more...

“2020/03” (2건)