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

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

BSD Socket
2020. 4. 8. 16:05

BSD Socket 사용법 정리


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


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

 

전체 코드


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

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

/* 유닉스 헤더 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()

/* Socket */
#include <arpa/inet.h> // htons()
#include <sys/types.h> // socket
#include <sys/socket.h> // socket
#include <sys/un.h> // struct sockaddr_un

#define SCK_FILE "/tmp/codingcat.ipc"
#define SCK_BUFF 512
#define SCK_MESG "Hello, IPC Client!"

void * start_routine(void * pfdclient); // 클라이언트가 접속할 때마다 새 스레드를 생성하여 실행할 함수이다.
void cleanup_routine(void * pfdsocket); // 소켓 함수 실행 중 오류가 발생할 때 수행할 공통의 해제 작업이 기술된 함수이다.

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

	int fdserver, fdclient; // 서버 소켓 및 클라이언트 소켓에 대한 파일 디스크럽터이다.
	socklen_t cbaddrserver, cbaddrclient; // 서버 및 클라이언트에 대한 인터넷 주소의 길이를 보관한다.
	struct sockaddr_un addrserver, addrclient; // 서버 및 클라이언트에 대한 인터넷 주소를 보관한다.
	int sockresult; // 소켓 함수의 실행 결과를 보관한다.

	thself = pthread_self(); // 현재 실행중인 자기 자신의 스레드 번호를 가져온다.
	pthread_cleanup_push(cleanup_routine, &fdclient); // 소켓 함수 실행 중 오류가 발생하였을 때 cleanup_routine을 실행한다.
	
    /* 서버 측 소켓을 생성한다. */
	fdserver = socket(AF_UNIX, SOCK_STREAM, 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_un); // 프로세스 주소의 길이를 구한다.
    bzero(&addrserver, cbaddrserver); // sockaddr_un 구조체의 내용을 0으로 리셋한다.
    addrserver.sun_family = AF_UNIX; // 구조체 내용 구성 - 1. 주소가 IPC 주소이다.
	strncpy(addrserver.sun_path, SCK_FILE, (sizeof(addrserver.sun_path) / sizeof(addrserver.sun_path[1]))); // 구조체 내용 구성 - 2. IPC로 접속할 수 있는 파일의 주소를 복사한다.

	/* 앞서 구성한 구조체로 서버용 소켓에 바인딩한다. */
	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);

	/* 연결 요청을 수용하기 위한 대기열을 구성한다. */
	sockresult = listen(fdserver, SOMAXCONN);
	if (sockresult < 0) {
		// 대기열 구성에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] listen(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
    }
	printf("[SUCCESS] listen() @ pthread_t = %p\n", thself);

	while (1) {
		pthread_t thclient;
		int thresult;

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

		cbaddrclient = sizeof(struct sockaddr_un); // 클라이언트 측 인터넷 주소의 길이를 구한다.
		bzero(&addrclient, cbaddrclient); // sockaddr_in 구조체의 내용을 0으로 리셋한다.

		/* 클라이언트 측의 연결 요청이 있을 때까지 무한정 기다린다. */
		fdclient = accept(fdserver, (struct sockaddr *)&addrclient, &cbaddrclient);
		if (fdclient < 0) {
        	// 연결 대기에서 오류가 발생하면, errno를 출력한다.
			printf("[FAILURE] accept(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
            // 연결 대기 다시 한다(무한루프).
			continue;
        }
		printf("[SUCCESS] accept() @ pthread_t = %p\n", thself);

		// 클라이언트 연결이 확인되면, 후속 작업은 새 스레드에 맡긴다. (본 스레드는 연결 대기만 함)
		thresult = pthread_create(&thclient, NULL, start_routine, &fdclient);
		if (thresult < 0) {
        	// 새 스레드 생성에서 오류가 발생하면, errno를 출력한다.
			printf("[FAILURE] pthread_create(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
            // 연결 대기 다시 한다(무한루프).
			continue;
		}
		printf("[SUCCESS] pthread_create() -> %p @ pthread_t = %p\n", thclient, thself);
	}

	/* 위 루프가 무한루프이므로 이 이하는 직접 실행될 일은 없으나 형식적으로 적기만 함. */
	pthread_exit((void *)((intptr_t)errno)); // 현재 errno를 리턴하며 본 스레드 종료

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

	return 0;
}

/* 
 * 새 스레드가 생성되면서 호출될 함수이다. 
 * pdfclient : 클라이언트 측 소켓에 대한 파일 디스크럽터이다.
 */
void * start_routine(void * pfdclient) {
	pthread_t thself;  // 새 스레드의 번호를 보관한다.

	int fdclient; // 클라이언트 측 소켓에 대한 파일 디스크럽터를 보관한다.
	int sockresult; // 소켓 함수 실행 결과를 보관한다.

	size_t cbbuffserver, cbbuffclient; // 서버에서 클라이언트로 보내는 문자열의 길이와 클라이언트에서 서버로 수신된 문자열의 길이를 보관한다.
	char buffserver[SCK_BUFF], buffclient[SCK_BUFF]; // 서버에서 클라이언트로 보내는 문자열과 클라이언트에서 서버로 수신된 문자열을 보관한다.
	ssize_t rwresult; // 송수신한 결과(바이트 수)를 보관한다.

	fdclient = *((int *)pfdclient); // 지정된 주소에서 클라이언트 파일 디스크럽터를 복사해 온다.

	thself = pthread_self(); // 현재 실행 중인 스레드 번호를 가져온다.
	pthread_cleanup_push(cleanup_routine, pfdclient); // 소켓 함수에 오류가 발생했을 때 cleanup_routine을 호출한다.

	/* 서버에서 클라이언트로 보낼 문자열을 구성한다. 서버의 현 스레드 번호를 문자열로 알려준다. */
	cbbuffserver = SCK_BUFF * sizeof(char);
	bzero(buffserver, cbbuffserver);
	sprintf(buffserver, "%s @ pthread_t = %p", SCK_MESG, thself);

	/* 서버에서 클라이언트로 문자열을 보낸다. */
	cbbuffserver = strlen(buffserver) * sizeof(char);
	rwresult = write(fdclient, buffserver, cbbuffserver);
	if (rwresult < 0) {
		// 송신에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] write(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] write(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
	printf(">> %s\n", buffserver);

	/* 클라이언트로부터 문자열을 수신할 버퍼를 구성한다. */
	cbbuffclient = SCK_BUFF * sizeof(char);
	bzero(buffclient, cbbuffclient);

	/* 클라이언트로부터 문자열을 받는다. */
	rwresult = read(fdclient, buffclient, cbbuffclient);
	cbbuffclient = strlen(buffclient) * sizeof(char);
	if (rwresult < 0) {
		// 수신에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] read(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] read(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
	printf(">> %s\n", buffclient);

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

	return (void *)((intptr_t)errno);
}

/*
 * 소켓 함수에 오류가 발생했을 경우 수행할 공통의 정리 작업이다.
 * pfdsocket : 오류가 발생하여 닫아야 할 소켓이다.
 */
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);
		}
		printf("[SUCCESS] close() @ pthread_t = %p\n", thself);
	}

	return;
}

실행 결과 보기


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

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

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

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

ID가 0x800681000인 thread가 무한루프를 돌리며 접속을 기다리다가 클라이언트로부터 접속이 발생하면 새 thread를 생성되면서 해당 클라이언트와 통신하게 된다. 여기서는 0x800681000로부터 0x800681500가 파생되고, 0x800681500를 통해 클라이언트가 통신한다. 이 과정은 다음과 같이 화면으로 출력된다.

클라이언트로부터 접속이 발생할 때마다 새 thread를 생성하여 통신한다.

IPC 서버 프로그램을 실행한 후에는 다음과 같이 소켓으로 지정한 경로에 파일이 생성된 것을 확인할 수가 있다.

$ ls -al | grep 파일이름

해당 경로에 실제로 임시 파일이 생성되었음을 확인할 수 있다.

카테고리 “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_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...
썸네일 이미지
[BSD Socket] FreeBSD(+*nix) 버전 TCP 클라이언트측 코드
BSD Socket 사용법 정리 FreeBSD(+*nix) 버전 클라이언트측 코드 본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 TCP 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. 1. 2. 16:41

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

BSD Socket
2020. 1. 2. 16:41

BSD Socket 사용법 정리


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


본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 TCP 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 "127.0.0.1"
#define SCK_PORT 9999
#define SCK_BUFF 512
#define SCK_MESG "Hello, TCP 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_STREAM, 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 매크로상수에 지정된 포트 번호로 통신한다.

	/* 서버에 접속한다. */
	sockresult = connect(fdserver, (struct sockaddr *)&addrserver, cbaddrserver);
	if (sockresult < 0) {
    	// 서버 접속에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] connect(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] connect() @ pthread_t = %p\n", thself);

	cbbuffserver = sizeof(buffserver);
	bzero(buffserver, cbbuffserver);

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

	cbbuffclient = sizeof(buffclient);
	bzero(buffclient, cbbuffclient);
	sprintf(buffclient, "%s @ pthread_t = %p", SCK_MESG, thself);

	/* 서버에 문자열을 송신한다. */
	cbbuffclient = strlen(buffclient) * sizeof(char);
	rwresult = write(fdserver, buffclient, cbbuffclient);
	if (rwresult < 0) {
    	// 송신에 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] write(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] write(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
	printf("<< %s\n", buffclient);
	
	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) 버전 TCP 서버측 코드
BSD Socket 사용법 정리 FreeBSD(+*nix) 버전 TCP 서버측 코드 본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 TCP 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..
BSD Socket
2020. 1. 1. 11:16

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

BSD Socket
2020. 1. 1. 11:16

BSD Socket 사용법 정리


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


본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 TCP 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 9999
#define SCK_BUFF 512
#define SCK_MESG "Hello, TCP Client!"

void * start_routine(void * pfdclient); // 클라이언트가 접속할 때마다 새 스레드를 생성하여 실행할 함수이다.
void cleanup_routine(void * pfdsocket); // 소켓 함수 실행 중 오류가 발생할 때 수행할 공통의 해제 작업이 기술된 함수이다.

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

	int fdserver, fdclient; // 서버 소켓 및 클라이언트 소켓에 대한 파일 디스크럽터이다.
	socklen_t cbaddrserver, cbaddrclient; // 서버 및 클라이언트에 대한 인터넷 주소의 길이를 보관한다.
	struct sockaddr_in addrserver, addrclient; // 서버 및 클라이언트에 대한 인터넷 주소를 보관한다.
	int sockresult; // 소켓 함수의 실행 결과를 보관한다.

	thself = pthread_self(); // 현재 실행중인 자기 자신의 스레드 번호를 가져온다.
	pthread_cleanup_push(cleanup_routine, &fdclient); // 소켓 함수 실행 중 오류가 발생하였을 때 cleanup_routine을 실행한다.
	
    /* 서버 측 소켓을 생성한다. */
	fdserver = socket(AF_INET, SOCK_STREAM, 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);

	/* 연결 요청을 수용하기 위한 대기열을 구성한다. */
	sockresult = listen(fdserver, SOMAXCONN);
	if (sockresult < 0) {
		// 대기열 구성에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] listen(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
    }
	printf("[SUCCESS] listen() @ pthread_t = %p\n", thself);

	while (1) {
		pthread_t thclient;
		int thresult;

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

		cbaddrclient = sizeof(struct sockaddr_in); // 클라이언트 측 인터넷 주소의 길이를 구한다.
		bzero(&addrclient, cbaddrclient); // sockaddr_in 구조체의 내용을 0으로 리셋한다.

		/* 클라이언트 측의 연결 요청이 있을 때까지 무한정 기다린다. */
		fdclient = accept(fdserver, (struct sockaddr *)&addrclient, &camp;baddrclient);
		if (fdclient < 0) {
        	// 연결 대기에서 오류가 발생하면, errno를 출력한다.
			printf("[FAILURE] accept(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
            // 연결 대기 다시 한다(무한루프).
			continue;
        }
		printf("[SUCCESS] accept() @ pthread_t = %p\n", thself);

		// 클라이언트 연결이 확인되면, 후속 작업은 새 스레드에 맡긴다. (본 스레드는 연결 대기만 함)
		thresult = pthread_create(&thclient, NULL, start_routine, &fdclient); // 새 스레드에서 startroutine(fdclient) 호출
		if (thresult < 0) {
        	// 새 스레드 생성에서 오류가 발생하면, errno를 출력한다.
			printf("[FAILURE] pthread_create(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
            // 연결 대기 다시 한다(무한루프).
			continue;
		}
		printf("[SUCCESS] pthread_create() -> %p @ pthread_t = %p\n", thclient, thself);
	}

	/* 위 루프가 무한루프이므로 이 이하는 직접 실행될 일은 없으나 형식적으로 적기만 함. */
	pthread_exit((void *)((intptr_t)errno)); // 현재 errno를 리턴하며 본 스레드 종료

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

	return 0;
}

/* 
 * 새 스레드가 생성되면서 호출될 함수이다. 
 * pdfclient : 클라이언트 측 소켓에 대한 파일 디스크럽터이다.
 */
void * start_routine(void * pfdclient) {
	pthread_t thself; // 새 스레드의 번호를 보관한다.

	int fdclient; // 클라이언트 측 소켓에 대한 파일 디스크럽터를 보관한다.
	int sockresult; // 소켓 함수 실행 결과를 보관한다.

	size_t cbserver, cbclient; // 서버에서 클라이언트로 보내는 문자열의 길이와 클라이언트에서 서버로 수신된 문자열의 길이를 보관한다.
	char buffserver[SCK_BUFF], buffclient[SCK_BUFF]; // 서버에서 클라이언트로 보내는 문자열과 클라이언트에서 서버로 수신된 문자열을 보관한다.
	ssize_t rwresult; // 송수신한 결과(바이트 수)를 보관한다.

	fdclient = *((int *)pfdclient); // 지정된 주소에서 클라이언트 파일 디스크럽터를 복사해 온다.

	thself = pthread_self(); // 현재 실행 중인 스레드 번호를 가져온다.
	pthread_cleanup_push(cleanup_routine, pfdclient); // 소켓 함수에 오류가 발생했을 때 cleanup_routine을 호출한다.

	/* 서버에서 클라이언트로 보낼 문자열을 구성한다. 서버의 현 스레드 번호를 문자열로 알려준다. */
	cbserver = SCK_BUFF * sizeof(char);
	bzero(buffserver, cbserver);
	sprintf(buffserver, "%s @ pthread_t = %p", SCK_MESG, thself);

	/* 서버에서 클라이언트로 문자열을 보낸다. */
	cbserver = strlen(buffserver) * sizeof(char);
	rwresult = write(fdclient, buffserver, cbserver);
	if (rwresult < 0) {
		// 송신에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] write(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] write(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
	printf("<< %s\n", buffserver);

	/* 클라이언트로부터 문자열을 수신할 버퍼를 구성한다. */
	cbclient = SCK_BUFF * sizeof(char);
	bzero(buffclient, cbclient);

	/* 클라이언트로부터 문자열을 받는다. */
	rwresult = read(fdclient, buffclient, cbclient);
	cbclient = strlen(buffclient) * sizeof(char);
	if (rwresult < 0) {
		// 수신에서 오류가 발생하면, errno를 출력한다.
		printf("[FAILURE] read(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
        // cleanup_routine(errno)를 실행한 뒤 본 스레드를 종료한다.
		pthread_exit((void *)((intptr_t)errno));
	}
	printf("[SUCCESS] read(): %zd byte(s) @ pthread_t = %p\n", rwresult, thself);
	printf(">> %s\n", buffclient);

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

	return (void *)((intptr_t)errno);
}

/*
 * 소켓 함수에 오류가 발생했을 경우 수행할 공통의 정리 작업이다.
 * pfdsocket : 오류가 발생하여 닫아야 할 소켓이다.
 */
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 함수를 호출하는 부분에서 '정의되지 않은 식별자' 오류를 낼 것이다.

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

ID가 0x800681000인 thread가 무한루프를 돌리며 접속을 기다리다가 클라이언트로부터 접속이 발생하면 새 thread를 생성되면서 해당 클라이언트와 통신하게 된다. 여기서는 0x800681000로부터 0x800681500가 파생되고, 0x800681500를 통해 클라이언트가 통신한다. 이 과정은 다음과 같이 화면으로 출력된다.

클라이언트로부터 접속이 발생할 때마다 새 thread를 생성하여 통신한다.

카테고리 “BSD Socket”
more...
썸네일 이미지
[단편 FreeBSD 사용법] FTP 서버 설정하기
단편 FreeBSD 사용법 FTP 서버 설정하기 이번 포스팅에서는 FreeBSD에서 FTP 서버를 구축하고 외부에서 접속을 테스트해보겠다. inetd 활성화하기 FreeBSD에는 FTP, HTTP, SSH, Telnet 등의 기본적인 서버가 내장되어 있으므로 설정을 통해 이를 활성화하기만 하면 된다. # vi /etc/inetd.conf 위 명령을 실행하여 미리 구성된 설정 파일에서 FTP 설정에 붙은 주석을 제거한다. 3번째 열에 tcp로 적힌 행은 IPv4로 접속 가능한 FTP 서버를 의미하고, tcp6으로 적힌 행은 IPv6로 접속 가능한 FTP 서버를 의미한다. 둘 중 하나, 또는 둘 다 주석을 제거하면 해당 프로토콜로 FTP 서버가 구축된다. 그 다음 rc.conf를 열어 inetd를 사용 가능..
Operating System/Unix × FreeBSD
2019. 12. 27. 11:14

[단편 FreeBSD 사용법] FTP 서버 설정하기

Operating System/Unix × FreeBSD
2019. 12. 27. 11:14

단편 FreeBSD 사용법


 

FTP 서버 설정하기


이번 포스팅에서는 FreeBSD에서 FTP 서버를 구축하고 외부에서 접속을 테스트해보겠다.

 

inetd 활성화하기


FreeBSD에는 FTP, HTTP, SSH, Telnet 등의 기본적인 서버가 내장되어 있으므로 설정을 통해 이를 활성화하기만 하면 된다.

# vi /etc/inetd.conf

 

위 명령을 실행하여 미리 구성된 설정 파일에서 FTP 설정에 붙은 주석을 제거한다. 3번째 열에 tcp로 적힌 행은 IPv4로 접속 가능한 FTP 서버를 의미하고, tcp6으로 적힌 행은 IPv6로 접속 가능한 FTP 서버를 의미한다. 둘 중 하나, 또는 둘 다 주석을 제거하면 해당 프로토콜로 FTP 서버가 구축된다.

 

그 다음 rc.conf를 열어 inetd를 사용 가능하게 수정 후 이를 실행한다.

# vi /etc/rc.conf

 

 

# /etc/rc.d/inetd start

 

 

로컬에서 테스트하기


다음과 같이 ftplocalhost에 접속해본다.

$ ftp localhost

 

 

방화벽 FTP 포트 개방하기


외부에서 접속할 수 있도록 방화벽의 FTP 포트를 개방한다. FTP 포트 번호는 TCP 21번이다.

다음은 임의의 IP를 가진 외부에서 서버의 21번 포트로 들어오는 패킷을 허용하는 명령이다. NIC는 네트워크 카드이며 ifconfig으로 인터넷에 접속된 네트워크 카드의 이름을 확인할 수 있다.

# ipfw -add allow tcp from any to any 21 in via /* NIC */ setup keep-state

 

예를 들어,

# ipfw -add allow tcp from any to any 21 in via em0 setup keep-state

 

다음은 서버의 21번 포트에서 접속자로 나가는 패킷을 허용하는 명령이다.

# ipfw -add allow tcp from any to any 21 out via /* NIC */ setup keep-state

 

예를 들어,

# ipfw -add allow tcp from any to any 21 out via em0 setup keep-state

 

위 명령은 ipfw의 rules 파일에 적어 놓으면 재부팅할 때에 편리하다.

 

외부에서 테스트하기


외부에서 테스트하여 로컬에서 테스트한 것과 같은지 확인한다.

 

카테고리 “Operating System/Unix × FreeBSD”
more...
썸네일 이미지
[단편 FreeBSD 사용법] bash 설치 및 사용하기
단편 FreeBSD 사용법 bash 설치 및 사용하기 FreeBSD는 기본적으로 csh를 기본 shell로 사용한다. 루트와 일반 사용자의 shell을 bash로 바꾸어보겠다. bash 설치 sudo 또는 루트 계정에서 bash 패키지를 설치한다. # pkg install bash 각 사용자에서 bash 적용 먼저 which 명령을 실행하여 bash가 어디에 설치되어 있는지 확인한다. 본 포스팅의 경우 /usr/local/bin/bash가 검색된다. # which bash 루트 사용자에게 bash를 적용하려면 아래와 같이 실행한다. chsh는 Change Shell의 약어이다. -s는 지정된 경로에 있는 shell로 바꾸라는 옵션이다. 앞서 확인한 /usr/local/bin/bash를 입력하면 된다. #..
Operating System/Unix × FreeBSD
2019. 12. 24. 19:13

[단편 FreeBSD 사용법] bash 설치 및 사용하기

Operating System/Unix × FreeBSD
2019. 12. 24. 19:13

단편 FreeBSD 사용법


bash 설치 및 사용하기


FreeBSD는 기본적으로 csh를 기본 shell로 사용한다. 루트와 일반 사용자의 shell을 bash로 바꾸어보겠다.

 

bash 설치


sudo 또는 루트 계정에서 bash 패키지를 설치한다.

# pkg install bash

 

 

각 사용자에서 bash 적용


먼저 which 명령을 실행하여 bash가 어디에 설치되어 있는지 확인한다. 본 포스팅의 경우 /usr/local/bin/bash가 검색된다.

# which bash

 

루트 사용자에게 bash를 적용하려면 아래와 같이 실행한다. chsh는 Change Shell의 약어이다. -s는 지정된 경로에 있는 shell로 바꾸라는 옵션이다. 앞서 확인한 /usr/local/bin/bash를 입력하면 된다.

# chsh -s /usr/local/bin/bash

 

루트가 아닌 특정 사용자에게 bash를 적용하려면 위 명령 끝에 사용자명을 적어주면 된다.

# chsh -s /usr/local/bin/bash <사용자>

 

 

로그아웃 후 다시 로그인하면 다음과 같이 shell이 리눅스의 것과 같은 bash로 적용되었음을 확인할 수 있다.

 

카테고리 “Operating System/Unix × FreeBSD”
more...
썸네일 이미지
[단막 FreeBSD 오류해결] FreeBSD 인터넷 연결
단막 FreeBSD 오류해결 FreeBSD 인터넷 연결 이 포스트는 FreeBSD의 인터넷 연결 오류를 해결한 개인의 경험을 정리한 것이다. 본 해법은 절대적인 해법이 아니기 때문에, 하드웨어 설정 상태에 따라 다양한 방법을 시도해 보아야 할 것이다. 문제 상황: 인터넷이 연결되지 않는다. ping, traceroute 모두 먹히지 않는다. 즉 인터넷 접속이 안 된다. FreeBSD에 NIC가 잡혀있는가 다음의 명령을 실행해 보았다. NIC가 제대로 잡혀있을뿐만 아니라 DHCP에 따라 공유기로부터 IP 주소(IPv4, IPv6 모두)까지 제대로 할당받았음을 확인 가능하다. # ifconfig DNS 설정이 양호한가 수 차례의 삽질 끝에 찾아낸 방법으로는, 무슨 이유에서인지는 알 수 없으나 FreeBSD에..
Operating System/Unix × FreeBSD
2019. 12. 24. 18:40

[단막 FreeBSD 오류해결] FreeBSD 인터넷 연결

Operating System/Unix × FreeBSD
2019. 12. 24. 18:40

단막 FreeBSD 오류해결


FreeBSD 인터넷 연결


이 포스트는 FreeBSD의 인터넷 연결 오류를 해결한 개인의 경험을 정리한 것이다. 본 해법은 절대적인 해법이 아니기 때문에, 하드웨어 설정 상태에 따라 다양한 방법을 시도해 보아야 할 것이다.

 

문제 상황: 인터넷이 연결되지 않는다.


ping, traceroute 모두 먹히지 않는다. 즉 인터넷 접속이 안 된다.

 

FreeBSD에 NIC가 잡혀있는가


다음의 명령을 실행해 보았다. NIC가 제대로 잡혀있을뿐만 아니라 DHCP에 따라 공유기로부터 IP 주소(IPv4, IPv6 모두)까지 제대로 할당받았음을 확인 가능하다.

# ifconfig

 

 

DNS 설정이 양호한가

수 차례의 삽질 끝에 찾아낸 방법으로는, 무슨 이유에서인지는 알 수 없으나 FreeBSD에 DNS 로컬 캐싱 기능(다시 말해서 FreeBSD가 DNS 서버로 작동하면서 DB를 자체 운영하고 FreeBSD가 외부 인터넷으로 접속할 때 도메인을 자기 자신으로부터 참조하는 것)이 켜져 있으면 도메인을 통한 인터넷 접속이 불가할 수 있다. 이와 관계된 설정 파일이 /etc/resolv.conf를 열어보면 DNS 주소가 루프백(127.0.0.1)으로 되어 있고, 공유기의 아이피 주소가 주석처리 되어 있음을 확인할 수 있다.

 

원인을 찾았으니 이를 해결해보도록 한다. 아래 명령을 사용하여 DNS 로컬 캐싱을 끈다.

# sysrc local_unbound_enable="NO"
# service local_unbound stop

 

그리고나서 vi/etc/resolv.conf를 수정한다. 127.0.0.1를 참조하는 라인을 삭제하고, 공유기 아이피 주소에 붙은 주석을 해제한다.

이후 도메인을 통한 ping, traceroutehost 명령까지 잘 작동됨을 확인할 수 있다.

 

카테고리 “Operating System/Unix × FreeBSD”
more...
썸네일 이미지
[단막 FreeBSD 사용법] syslogd 콘솔 출력 끄기
단막 FreeBSD 사용법 syslogd 콘솔 출력 끄기 FreeBSD를 사용 중 콘솔에 갑작스럽게 syslogd 메시지가 뜬다면 사용이 불편할 수 있다. 이럴 때는 다음과 같이 syslogd의 콘솔 출력을 끌 수 있다. syslogd daemon 종료 및 부팅 시 자동 시작 방지 우선 syslogd daemon을 종료한다. $ su # /etc/rc.d/syslogd stop vi로 /etc/rc.conf를 연 다음 syslogd_enable="YES"라고 적힌 항목을 찾아 다음과 같이 수정한다. 해당 내용이 없을 경우 아래의 내용을 직접 적는다. syslogd_enable="NO" syslogd의 출력을 파일로 돌리기 앞서 설명한 방법은 syslogd를 완전히 종료하고 부팅 시 자동으로 실행되는 것까..
Operating System/Unix × FreeBSD
2019. 12. 22. 10:53

[단막 FreeBSD 사용법] syslogd 콘솔 출력 끄기

Operating System/Unix × FreeBSD
2019. 12. 22. 10:53

단막 FreeBSD 사용법


syslogd 콘솔 출력 끄기


FreeBSD를 사용 중 콘솔에 갑작스럽게 syslogd 메시지가 뜬다면 사용이 불편할 수 있다.

이럴 때는 다음과 같이 syslogd의 콘솔 출력을 끌 수 있다.

 

syslogd daemon 종료 및 부팅 시 자동 시작 방지


우선 syslogd daemon을 종료한다.

$ su
# /etc/rc.d/syslogd stop

 

 

vi/etc/rc.conf를 연 다음 syslogd_enable="YES"라고 적힌 항목을 찾아 다음과 같이 수정한다. 해당 내용이 없을 경우 아래의 내용을 직접 적는다.

syslogd_enable="NO"

 

 

syslogd의 출력을 파일로 돌리기


앞서 설명한 방법은 syslogd를 완전히 종료하고 부팅 시 자동으로 실행되는 것까지 막는 방법이다. 그러나 서버를 운영하고자 할 때 로그는 필요하기도 하므로, 콘솔 대신 파일로 출력하고자 할 때는 다음과 같이 수행한다.

우선 syslogd daemon을 종료한다.

$ su
# /etc/rc.d/syslogd stop

 

그 다음 vi/etc/syslog.conf 파일의 내용을 수정한다. 이미 syslogd의 출력을 파일로 출력할 수 있도록 내용이 적혀져 있으므로 이 라인에 붙은 주석 기호 #을 제거한다. 없을 경우 아래와 같이 입력한다.

console.info /var/log/console.log

 

그리고나서 해당 경로에 빈 텍스트 파일을 하나 만들고 퍼미션을 600이상 부여한다. 이는 /etc/syslog.conf의 주석에 지시사항으로 나와있다. 이후 중지했던 syslogd daemon을 시작하면 작업 완료다.

# touch /var/log/console.log
# chmod 600 /var/log/console.log
# /etc/rc.d/syslogd restart

 

 

카테고리 “Operating System/Unix × FreeBSD”
more...
썸네일 이미지
[단편 FreeBSD 사용법] 일반 계정에서 su, sudo 명령 사용하기
단편 FreeBSD 사용법 일반 계정에서 su, sudo 명령 사용하기 su명령은 일반 계정에서 루트 계정의 Shell을 띄울 때 사용하는 명령이다. FreeBSD에서는 일반 계정에서 su를 사용하고자 할 때, 다음과 같이 Sorry 메시지를 띄우며 루트 계정으로의 진입을 거부한다. 루트 계정에서 휠 사용자 추가하기 (첫번째 방법) 휠wheel은 서버에서 관리자 작업을 수행할 수 있는 사용자(계정)들을 말한다. 일반 계정이 su 명령을 사용하려면 그 계정을 휠 그룹에 추가해야 한다. 우선, 서버를 관리자 계정으로 로그인한다. 그리고 다음과 같이 파일 시스템(/)을 읽고 쓰기가 가능한 상태로 재마운트한다. # mount -u -w / -w 옵션은 지정된 포인트에 대해 읽고 쓸 수 있는 권한을 지정하여 마운..
Operating System/Unix × FreeBSD
2019. 12. 22. 01:07

[단편 FreeBSD 사용법] 일반 계정에서 su, sudo 명령 사용하기

Operating System/Unix × FreeBSD
2019. 12. 22. 01:07

단편 FreeBSD 사용법


일반 계정에서 su, sudo 명령 사용하기


su명령은 일반 계정에서 루트 계정의 Shell을 띄울 때 사용하는 명령이다. FreeBSD에서는 일반 계정에서 su를 사용하고자 할 때, 다음과 같이 Sorry 메시지를 띄우며 루트 계정으로의 진입을 거부한다.

 

루트 계정에서 휠 사용자 추가하기 (첫번째 방법)


wheel은 서버에서 관리자 작업을 수행할 수 있는 사용자(계정)들을 말한다. 일반 계정이 su 명령을 사용하려면 그 계정을 휠 그룹에 추가해야 한다. 우선, 서버를 관리자 계정으로 로그인한다. 그리고 다음과 같이 파일 시스템(/)을 읽고 쓰기가 가능한 상태로 재마운트한다.

# mount -u -w /

 

-w 옵션은 지정된 포인트에 대해 읽고 쓸 수 있는 권한을 지정하여 마운트하라는 뜻이다. -u는 이미 마운트된 포인트를 다시 마운트하라는 의미이다. 어차피 서버가 부팅될 때 파일 시스템(/)이 마운트되어야 하므로 이를 쓰기 가능한 상태로 재마운트한다는 의미이다. 윈도우로 치면, C:\ 위치에 파일을 추가하고 수정할 때 UAC 확인창이 뜨면서 관리자 권한으로 상승해야 하는 것과 비슷한 조치이다. 그러고 나서 다음 명령을 실행한다.

# pw usermod 계정 -G wheel

pw는 서버의 사용자 및 그룹을 추가/수정/관리하는데 사용되는 명령이다. pw usermod는 사용자를 수정한다는 의미이다. -G Wheel은 휠 사용자 명단에 해당 계정을 추가하라는 의미이다.

 

루트 계정에서 휠 사용자 추가하기 (두번째 방법)

먼저 루트 계정으로 로그인하고 다음과 같이 파일 시스템을 쓰기 가능한 상태로 재마운트 한다.

# mount -u -w /

 

그리고나서 vi/etc/group파일에 다음과 같은 내용을 추가한다.

wheel:*:0:계정

 

아마도 해당 라인에는 이미 루트 계정이 들어가 있을 것이다. 이 때는 컴마를 붙이고 곧바로 휠 사용자로 지정할 계정들을 적는다.

wheel:*:0:root,계정,계정,...

 

재부팅 후 사용


서버를 재부팅하고 일반 계정으로 로그인 후 su를 실행하면 루트 계정으로 쉘이 진입함을 확인할 수 있다. exit를 입력하여 루트 계정을 빠져나갈 수 있다. 한번 더 입력하면 일반 계정도 로그아웃되어 최초 로그인 화면으로 넘어간다.

 

sudo 기능 사용하기


sudo는 아래 명령을 실행하여 패키지를 설치해야 한다.

# pkg install sudo

 

visudo를 실행하여 약 90번째 라인에 있는 주석을 제거한다. 휠 사용자는 sudo를 이용할 수 있게 하는 것인데 만일 특정 사용자에게만 sudo를 이용할 수 있게 하려면 다음과 같은 형식으로 한 줄씩 추가하면 된다.

<사용자> ALL=(ALL) ALL

 

 

루트 사용자에서 빠져 나온 후 다음과 같이 sudo를 실행하여 잘 작동하는지 확인할 수 있다.

 

카테고리 “Operating System/Unix × FreeBSD”
more...
썸네일 이미지
[Windows API] 크리티컬 섹션을 사용하여 스레드간 변수 공유
Windows API 본 시리즈에서는 Windows API 개별 함수에 대해 간략한 사용법 및 응용에 대해 정리한다. 이전 게시글: 스레드의 생존 여부 확인하기 다음 게시글: OutputDebugString을 printf처럼 서식(포맷) 적용하여 사용하기 크리티컬 섹션을 사용하여 스레드간 변수 공유 본 포스팅에서는 크리티컬 섹션(CRITICAL_SECTION) 구조체를 사용하여 스레드간 변수를 공유하는 방법에 대해 정리한다. CRITICAL_SECTION 스레드간 공유하고자 하는 변수와 함께 CRITICAL_SECTION 구조체 변수를 선언한다. /* 스레드간 공유하고자 하는 정수형 변수 */ DWORD g_dwInteger = 0; /* 상기 정수형 변수에 접근하려면 아래 구조체를 통해 권한을 얻어야 ..
API/Windows API
2019. 12. 19. 11:19

[Windows API] 크리티컬 섹션을 사용하여 스레드간 변수 공유

API/Windows API
2019. 12. 19. 11:19

Windows API


본 시리즈에서는 Windows API 개별 함수에 대해 간략한 사용법 및 응용에 대해 정리한다.

이전 게시글: 스레드의 생존 여부 확인하기

다음 게시글: OutputDebugString을 printf처럼 서식(포맷) 적용하여 사용하기

 

크리티컬 섹션을 사용하여 스레드간 변수 공유


본 포스팅에서는 크리티컬 섹션(CRITICAL_SECTION) 구조체를 사용하여 스레드간 변수를 공유하는 방법에 대해 정리한다.

 

CRITICAL_SECTION


스레드간 공유하고자 하는 변수와 함께 CRITICAL_SECTION 구조체 변수를 선언한다.

/* 스레드간 공유하고자 하는 정수형 변수 */
DWORD g_dwInteger = 0;
/* 상기 정수형 변수에 접근하려면 아래 구조체를 통해 권한을 얻어야 한다. */
CRITICAL_SECTION g_stCriticalSection;

 

InitializeCriticalSection


CRITICAL_SECTION 구조체를 사용하기 위해서는 반드시 리셋되어야 한다. 구조체를 리셋하는 함수는 InitializeCriticalSection이다.

VOID InitializeCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection 
);

 

lpCriticalSection 매개변수를 통해 리셋할 CRITICAL_SECTION을 지정한다.

InitializeCriticalSection(&g_stCriticalSection);

 

EnterCriticalSection, LeaveCriticalSection


VOID EnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection  
);
BOOL TryEnterCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);
VOID LeaveCriticalSection(
    LPCRITICAL_SECTION lpCriticalSection
);

스레드간 공유 변수에 접근할 때 EnterCriticalSection으로 크리티컬 섹션에 진입하고 공유 변수 사용이 끝나면 LeaveCriticalSection으로 크리티컬 섹션을 벗어난다. EnterCriticalSection은 타 스레드에서 크리티컬 섹션을 사용하고 있을 때 대기한다. 대기과정(현재 스레드의 중지) 없이 즉시 사용중 여부를 확인하고자 하면 TryEnterCriticalSection을 사용한다. 임계영역에 진입했다면 TRUE를 반환하고 그렇지 못했다면 FALSE를 반환한다.

EnterCriticalSection(&g_stCriticalSection);
{
    /* ... g_dwInteger에 대한 읽고 쓰기 */
}
LeaveCriticalSection(&g_stCriticalSection);

 

실행 예제


다음은 크리티컬 섹션의 실행 예이다. 창에 대한 스레드, 콘솔창에 대한 스레드 총 2개의 스레드가 실행중이며 콘솔창으로 1부터 30까지 센 후 콘솔창은 닫힌다. 창의 버튼을 클릭하면 콘솔창에서 세고 있는 숫자는 1로 리셋된다. 이 과정에서 크리티컬 섹션이 사용된다.

크리티컬 섹션을 사용하여 공유 변수가 수정될 수 있다.
카테고리 “API/Windows API”
more...
썸네일 이미지
[단막 Windows API] 스레드의 생존 여부 확인하기
단막 Windows API 스레드의 생존 여부 확인하기 본 포스팅에서는 특정 핸들(HANDLE)에 대한 스레드의 생존 여부, 다시 말해서 현재 실행중인지 종료되었는지 여부를 확인하는 방법을 정리한다. GetExitCodeThread 스레드의 생존 여부를 확인할 수 있는 함수로는 GetExitCodeThread가 있다. 이 함수의 원형은 다음과 같다. BOOL GetExitCodeThread( HANDLE hThread, LPDWORD lpExitCode ); hThread는 생존 여부를 확인하고자 하는 스레드의 핸들(HANDLE)이다. lpExitCode를 통해 특정 스레드의 생존 또는 종료 코드를 얻을 수 있다. 생존 여부를 확인할 수 있으면 TRUE를 반환하고 그렇지 않으면(즉, 오류가 발생하면) F..
API/Windows API
2019. 12. 18. 22:57

[단막 Windows API] 스레드의 생존 여부 확인하기

API/Windows API
2019. 12. 18. 22:57

단막 Windows API


스레드의 생존 여부 확인하기


본 포스팅에서는 특정 핸들(HANDLE)에 대한 스레드의 생존 여부, 다시 말해서 현재 실행중인지 종료되었는지 여부를 확인하는 방법을 정리한다.

 

GetExitCodeThread


스레드의 생존 여부를 확인할 수 있는 함수로는 GetExitCodeThread가 있다. 이 함수의 원형은 다음과 같다.

BOOL GetExitCodeThread(
    HANDLE hThread,
    LPDWORD lpExitCode
);

 

hThread는 생존 여부를 확인하고자 하는 스레드의 핸들(HANDLE)이다. lpExitCode를 통해 특정 스레드의 생존 또는 종료 코드를 얻을 수 있다.

생존 여부를 확인할 수 있으면 TRUE를 반환하고 그렇지 않으면(즉, 오류가 발생하면) FALSE를 반환한다. 이 때 오류 내용은 GetLastError 함수로 확인 가능하다.

스레드의 생존 여부를 위해 GetExitCodeThread를 호출하는 예이다.

BOOL fResult = FALSE;
DWORD dwExitCode = 0;
/* ... */
fResult = GetExitCodeThread(/* THREAD */, &dwExitCode);

 

위와 같이 호출하여 dwExitCode의 값이 STILL_ACTIVE이면 해당 스레드는 현재 실행중인 상태임을 의미한다. 해당 스레드가 종료되었다면, 종료 당시 스레드 프로시저가 리턴한 값이 보관된다. 여기서 알 수 있는 사실은, 스레드 프로시저를 작성할 때 종료 코드를 STILL_ACTIVE와 중복시키면 안 된다는 사실이다. STILL_ACTIVE의 실제 값은 정수 259이다.

 

실행 예제


static TCHAR szTextBuffer[4096];
HANDLE hThread = /* CreateThread(...) */;
DWORD dwErrorCode = 0;
/* ... */
GetExitCodeThread(hThread, &dwErrorCode);
if (dwErrorCode == STILL_ACTIVE) {
    _stprintf(szTextBuffer, TEXT("WM_USER: hThread = %08p, dwErrorCode = STILL_ACTIVE\n"), hThread);
} else {
    _stprintf(szTextBuffer, TEXT("WM_USER: hThread = %08p, dwErrorCode = %08lx\n"), hThread, dwErrorCode);
}
OutputDebugString(szTextBuffer);
/* ... */

 

버튼으로 채워진 창을 하나 열고, 이 창이 열릴 때 오랜 시간이 걸리는 작업을 수행하는 콘솔창도 하나 더 띄운다. 이 콘솔창은 스레드가 종료되면 자동으로 닫힌다. 버튼을 클릭하면 GetExitCodeThread을 실행하여 해당 스레드의 생존 여부를 디버그 출력창으로 보일 것이다.

스레드가 실행중일 때는 STILL_ACTIVE 값이 반환된다.

 

스레드가 종료되었을 때는 해당 프로시저의 리턴 값을 알 수 있다.
카테고리 “API/Windows API”
more...
썸네일 이미지
[단막 Windows API] CreateThread로 스레드 생성하기
단막 Windows API CreateThread로 스레드 생성하기 본 포스팅에서는 CreateThread를 사용하여 스레드를 생성하는 간단한 예를 정리한다. 새 스레드에서 실행할 프로시저 형식 새 스레드에서 호출할 프로시저의 형식(시그니처)은 다음과 같이 정의한다. DWORD (LPVOID lpParam); lpParam 매개변수는 CreateThread 함수를 실행할 때 스레드로 전달될 수 있다. 위 형식을 따라 콘솔 창을 열고 1부터 30까지 세는데 1,000 밀리세컨드마다 지연시키는 프로시저를 작성해 보겠다. lpParam에는 이 스레드를 만든 부모 창 HWND를 전달한다고 약속한다. DWORD ThreadProc(LPVOID lpParam) { HWND hWnd = (HWND)lpParam; /..
API/Windows API
2019. 12. 15. 20:13

[단막 Windows API] CreateThread로 스레드 생성하기

API/Windows API
2019. 12. 15. 20:13

단막 Windows API


CreateThread로 스레드 생성하기


본 포스팅에서는 CreateThread를 사용하여 스레드를 생성하는 간단한 예를 정리한다.

 

새 스레드에서 실행할 프로시저 형식


새 스레드에서 호출할 프로시저의 형식(시그니처)은 다음과 같이 정의한다.

DWORD <NAME>(LPVOID lpParam);

 

lpParam 매개변수는 CreateThread 함수를 실행할 때 스레드로 전달될 수 있다.

위 형식을 따라 콘솔 창을 열고 1부터 30까지 세는데 1,000 밀리세컨드마다 지연시키는 프로시저를 작성해 보겠다. lpParam에는 이 스레드를 만든 부모 창 HWND를 전달한다고 약속한다.

DWORD ThreadProc(LPVOID lpParam) {
    HWND hWnd = (HWND)lpParam;
    /* 새 콘솔창을 띄운다. */
    AllocConsole();
    
    /* 새 콘솔창에 맞추어 표준 입출력 핸들을 다시 연다. */
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
    
    printf("Console Created!\n");
    
    /* 1000 밀리세컨드의 간격을 두고 1부터 30까지 출력한다. */
    while (TRUE) {
        static int count = 0;
        
        printf("count = %d\n", ++count);
        Sleep(1000);
        
        if (count >= 30) break;
    }
    
    /* 5000 밀리세컨드의 여유를 두고 콘솔창을 닫는다. */
    Sleep(5000);
    FreeConsole();
    
    /* 이 스레드를 종료한다. */
    return 0;
}

 

새 스레드 생성 및 실행


CreateThread의 원형은 다음과 같다.

HANDLE CreateThread(
    LPSECURITY_ATTRIBUTES   lpThreadAttributes,
    DWORD                   dwStackSize,
    LPTHREAD_START_ROUTINE  lpStartAddress,
    LPVOID                  lpParameter,
    DWORD                   dwCreationFlags,
    LPDWORD                 lpThreadId
);

 

매개변수에 대한 자세한 내용은 MSDN을 참조한다. 여기에서는 간략한 사용법만을 보기 위해 각종 보안 코드들은 생략한다. 다음은 가장 간단한 호출법이다. lpStartAddress(3번째 매개변수)에 앞서 작성한 프로시저를 지정하고, lpThreadId(마지막 매개변수)에 새로 생성된 스레드의 ID값이 전달된다. 반환되는 값은 생성된 스레드의 핸들(HANDLE)이다.

hThread = CreateThread(NULL, 0, [PROCEDURE], (LPVOID)[PARAMETER], 0, &[THREAD ID]);

 

다음은 실제 소스에 작성한 예이다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {
    /* 중략 */
    switch (uMessage) {
    /* 중략 */
    case WM_CREATE:
    {
        /* 창을 생성할 때 스레드도 함께 생성한다. */
        HANDLE hThread = NULL;
        DWORD dwThreadId = 0;
        
        hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)hWnd, 0, &dwThreadId);
    }
    break;
    /* 중략 */
    }
    /* 중략 */
}

 

실행 결과


프로그램을 실행하면 빈 창과 함께 콘솔창이 출력된다. 콘솔창이 1부터 30까지 세고 닫히는 동안 창에서는 GUI 이벤트를 독립적으로 수행할 수 있다.

1부터 30까지 센 다음 콘솔창을 닫는다. 이 창의 스레드는 창의 스레드와 다르다.
카테고리 “API/Windows API”
more...
[Win32 문자열 종류와 상호 변환 방법] Part 3 - COM/.NET 문자열 (完)
Win32 C/C++ 문자열 종류와 상호 변환 방법 Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수도 없다. 본 포스트에서는 이전 포스트에 이어서 Win32 C/C++에서 사용하는 문자열의 종류 중 COM, .NET에서 사용하는 문자열에 대해 설명하고 상호 변환 방법에 대해 설명하겠다. 1. BSTR, __bstr_t, CComBSTR BSTRBasic String / Binary String은 COM에서 표준으로 사용하는 문자열이다. 이 문자열은 마지막에 NULL 문자가 붙지 않는 대신 문자열의 맨 앞에 4바이트 정수로 크기를 명시하는 특징이 있다. 그렇기 때문에 Win32 C/C++에서 문자열 리터럴을 B..
Microsoft Windows/Win32 문자열 처리
2019. 3. 11. 11:13

[Win32 문자열 종류와 상호 변환 방법] Part 3 - COM/.NET 문자열 (完)

Microsoft Windows/Win32 문자열 처리
2019. 3. 11. 11:13

Win32 C/C++ 문자열 종류와 상호 변환 방법


Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수도 없다. 본 포스트에서는 이전 포스트에 이어서 Win32 C/C++에서 사용하는 문자열의 종류 중 COM, .NET에서 사용하는 문자열에 대해 설명하고 상호 변환 방법에 대해 설명하겠다.

 

1. BSTR, __bstr_t, CComBSTR


BSTRBasic String / Binary String은 COM에서 표준으로 사용하는 문자열이다. 이 문자열은 마지막에 NULL 문자가 붙지 않는 대신 문자열의 맨 앞에 4바이트 정수로 크기를 명시하는 특징이 있다. 그렇기 때문에 Win32 C/C++에서 문자열 리터럴을 BSTR형 객체에 보관하기 위해서는 다음과 같이 SysAllocString으로 동적할당을 수행하며, 사용이 완료된 객체는 SysFreeString으로 해제한다. 다음은 BSTR의 선언이다.

typedef OLECHAR * BSTR;

 

여기서 혼동하지 말아야 할 것은, BSTR이 문자열 앞에 문자수를 동반하지만, BSTR이 가리키는 포인터는 문자열 그 자체라는 것이다. 문자수를 얻고자 한다면 해당 포인터에서 4바이트 앞을 참조하면 되지만, 항상 4바이트일 것이라는 억측을 하면 안되므로 SysStringLen 함수로 하여금 문자 수를 대신 가져오게 한다.

또한 문자열 앞에 문자열의 길이를 명시하므로 문자열의 끝에 반드시 NULL 문자가 붙을 필요는 없다. 다른 말로 문자열 끝에 NULL 문자가 있을 것이라는 억측을 하면 안 된다. 그러므로 BSTR을 C-Style로 변환하기 위해서 강제 형변환하는 것보다는, 별도로 제공된 함수를 사용하는 것이 안전하다.

 

BSTR SysAllocString(
    OPT IN const OLECHAR * psz
);
void SysFreeString(
    BSTR bstrString
);
UINT SysStringLen(
    OPT IN BSTR bstr
);

 

이 함수를 사용하여 OLECHAR 문자열을 BSTR로 변환하고, 사용이 끝난 문자열을 해제해 보겠다.

/* Example */
OLECHAR ocs[] = OLESTR("테스트 문자열.");
BSTR bstr;

/* ... */

bstr = SysAllocString(ocs);

// ... BSTR로 처리될 작업

SysFreeString(bstr);

/* ... */

 

BSTR에서 OLECHAR 문자열로 변환하기 위해, 앞서 설명된 매크로 함수를 사용할 수 있다.

/* Example */
#include <atlconv.h>

BSTR bstr;
LPTSTR tcs = NULL;

/* ... */

USES_CONVERSION;
tcs = OLE2T(bstr);

/* ... */

 

1-2. __bstr_t, CComBSTR

__bstr_t와 CComBSTR은 공통적으로 BSTR 형 문자열과 관련 함수들을 하나의 클래스로 캡슐화 한 것이다. 다만, __bstr_t는 Compiler COM Support에서 사용하는 자료형으로서 comutil.h 헤더파일에 정의되어 있고, CComBSTR은 ATL에서 사용하는 자료형으로서 atlbase.h에 정의되어 있다는 차이가 있다. 여기서 Compiler COM Support는 COM을 지원하는 라이브러리 파일을 소스 코드에서 #import 구문으로 포함시켰을 때 컴파일러가 자동으로 내부 클래스와 메서드를 선언하는 구문을 생성하는 것을 뜻한다.

 

2. System::String ^

System::String ^은 .NET Framework에서 지원하는 System.String 형을 C++/CLI에서 부르는 명칭이다. 다음과 같이 생성자로 Multibyte String과 Wide String의 두 가지를 받을 수 있고, 메모리 고정을 통해 Wide String으로 변환할 수 있다.

/* Example */
TCHAR tcs1[] = TEXT("안녕하세요.");
TCHAR tcs2[56] = { TEXT('\0'), };

System::String ^ gcs = nullptr;
pin_ptr pcs = nullptr;

gcs = gcnew System::String(tcs1); // C-Style에서 관리되는 문자열로 변환
pcs = PtrToStringChars(gcs); // 관리되는 문자열에서 C-Style 버퍼 얻기

// wchar_t *가 필요한 곳에 pcs를 대입 가능

 

추가적으로 관리되는 배열을 C-Style 배열로 변환하는 소스 패턴은 다음과 같다.

/* Example */
cli::array^ origin = gcnew cli::array(/* ... */);
cli::pin_ptr conv = &origin[0];

// ... conv를 포인터 배열처럼 사용

 

참고 자료

pin_ptr (C++/CLI) - MSDN
https://msdn.microsoft.com/ko-kr/library/1dz8byfh.aspx
How to convert from System::String* to Char* in Visual C++ - MSDN
https://support.microsoft.com/en-us/help/311259/how-to-convert-from-system-string-to-char-in-visual-c
Working with strings in COM, MFC, Win32, STL / Unraveling Strings in Visual C++
https://www.johndcook.com/blog/cplusplus_strings/

 

 

카테고리 “Microsoft Windows/Win32 문자열 처리”
more...
[Win32 문자열 종류와 상호 변환 방법] Part 2 - Windows API 문자열
Win32 C/C++ 문자열 종류와 상호 변환 방법 Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수도 없다. 본 포스트에서는 이전 포스트에 이어서 Win32 C/C++에서 사용하는 문자열의 종류 중 Windows API에서 사용하는 문자열에 대해 설명하고 상호 변환 방법에 대해 설명하겠다. 1. CHAR, LPSTR, LPCSTR, WCHAR, LPWSTR, LPCWSTR, TCHAR, LPTSTR, LPCTSTR 지금까지의 설명의 C/C++의 표준 문자열이었다면 지금부터는 Win32에서 쓰이는 문자열 형에 대한 설명이다. 우선, Windows API에서 기본적으로 쓰이는 문자형은 CHAR과 WCHAR이다. ..
Microsoft Windows/Win32 문자열 처리
2019. 3. 11. 11:03

[Win32 문자열 종류와 상호 변환 방법] Part 2 - Windows API 문자열

Microsoft Windows/Win32 문자열 처리
2019. 3. 11. 11:03

Win32 C/C++ 문자열 종류와 상호 변환 방법


Win32 C/C++에는 문자열을 나타내는 자료형이 다양하다. 그리고 그 자료형마다 용도와 역할이 있기 때문에 어느 하나만을 선택해서 사용할 수도 없다. 본 포스트에서는 이전 포스트에 이어서 Win32 C/C++에서 사용하는 문자열의 종류 중 Windows API에서 사용하는 문자열에 대해 설명하고 상호 변환 방법에 대해 설명하겠다.

 

1. CHAR, LPSTR, LPCSTR, WCHAR, LPWSTR, LPCWSTR, TCHAR, LPTSTR, LPCTSTR


지금까지의 설명의 C/C++의 표준 문자열이었다면 지금부터는 Win32에서 쓰이는 문자열 형에 대한 설명이다. 우선, Windows API에서 기본적으로 쓰이는 문자형은 CHAR과 WCHAR이다. 각각은 표준 자료형을 다르게 이름붙인 것이다.

typedef char CHAR;
typedef wchar_t WCHAR;

 

두 종류의 문자가 각각 NULL 문자로 끝나는 C-Style 문자열로 구성될 경우 이를 LPSTR, LPWSTR이라 한다.

typedef CHAR * LPSTR;
typedef WCHAR * LPWSTR;

 

LPSTR과 LPWSTR이 상수 속성을 가질 경우 LPCSTR와 LPCWSTR이라 한다.

typedef CONST CHAR * LPCSTR;
typedef CONST WCHAR * LPCWSTR;

 

마지막으로 Visual Studio(Visual C++) 컴파일러의 설정에 따라 유니코드를 지원하면 Widechar를 쓰고 지원하지 않으면 ANSI 문자열을 사용하도록 가변적으로 설정한 문자열이 TCHAR, LPTSTR, LPCTSTR이다. 그리고 문자열 리터럴이 이에 맞춰 지정되도록 TEXT 매크로가 사용된다.

/* TCHAR, LPTSTR, LPCTSTR */
#ifdef _UNICODE
    typedef WCHAR TCHAR;
    typedef LPWSTR LPTSTR;
    typedef LPCWSTR LPCTSTR;
    #define TEXT(str) L##str
#else
    typedef CHAR TCHAR;
    typedef LPSTR LPTSTR;
    typedef LPCSTR LPCTSTR;
    #define TEXT(str) str
#endif

 

1-1. CHAR과 WCHAR간 상호변환


일반적으로 Windows API는 A로 끝나는 ANSI형(예: DrawTextA)과 W로 끝나는 Widechar형(예: DrawTextW)를 모두 선언한 뒤 TCHAR 형으로 컴파일러에 따라 매크로에 의해 전환하는 형태(예: DrawText)를 갖고 있다. 하지만, 명시적으로 Widechar 문자열과 ANSI 문자열을 상호 변환하고자 한다면 다음과 같은 Windows API를 사용한다.

int MultiByteToWideChar(
    IN UINT CodePage,
    IN DWORD dwFlags,
    IN LPCSTR lpMultiByteStr,
    IN INT cbMultiByte,
    OPT OUT LPWSTR lpWideCharStr,
    IN INT cchWideChar
);
int WideCharToMultiByte(
    IN UINT CodePage,
    IN DWORD dwFlags,
    IN LPCWSTR lpWideCharStr,
    IN INT cchWideChar,
    OPT OUT LPSTR lpMultiByteStr,
    IN INT cbMultiByte,
    OPT IN LPCSTR lpDefaultChar,
    OPT OUT LPBOOL lpUsedDefaultChar
);

 

CodePage
Multibyte 문자열에 대해 어떤 코드페이지를 기준으로 인코드되었는지(인코드 할 것인지)를 지정하는 옵션이다.
CP_ACP
현재 Windows의 기본 코드 페이지를 사용한다.
CP_THREAD_ACP
본 프로그램이 실행되는 Thread에 할당된 코드 페이지를 적용한다.
CP_UTF7 또는 CP_UTF8
UTF-7 또는 UTF-8로 인코드한다. 이 옵션을 사용할 경우 lpDefaultCharlpUsedDefaultChar 매개변수는 항상 NULL이어야 한다.
dwFlags
각종 플래그 옵션이다.
WC_COMPOSITECHECK
강세가 붙는 문자에 대해 기본 알파벳 + 강세표시로 분리하여 인코드한다. 이 옵션을 명시하지 않을 경우 강세 붙는 문자 그대로 인코드된다.
lpWideCharStr
변환할 또는 변환될 문자열을 수용하는 Widechar형 문자열 버퍼이다.
cchWideChar
lpWideCharStr의 버퍼의 크기이다. 단위는 문자 수이다. 상기 문자열이 NULL 문자로 끝나는 C-Style 문자열이면 이 매개변수는 -1을 전달해도 무방하다.
lpMultiByteStr
변환할 또는 변환될 문자열을 수용하는 Multibyte형 문자열 버퍼이다.
cbMultiByte
lpMultiByteStr의 버퍼의 크기이다. 단위는 바이트이다. 이 매개변수의 값으로 0을 전달할 경우 변환 시 필요한 버퍼의 바이트 수가 반환되며 상기 Multibyte형 문자열 버퍼 매개변수는 무시된다.
lpDefaultChar
특정 WCHAR에 대응하는 Multibyte 문자가 없는 경우(예: 유니코드에는 있는데 KS-X-1001/1002에는 없는 글자) 대체할 기본 문자이다. 단, UTF-7/UTF-8로 변환되는 경우 이 매개변수는 반드시 NULL이어야 한다.
lpUsedDefaultChar
상기 lpDefaultChar를 한번이라도 사용하게 된 경우가 생겼다면 이 매개변수로 TRUE가 출력된다. 이 매개변수를 사용할 필요가 없을 경우 NULL로 지정해도 무방하며, UTF-7/UTF-8로 변환하는 경우 이 매개변수는 반드시 NULL이어야 한다.

 

다음은 WCHAR형 문자열에서 CHAR형 문자열로 변환하는 예이다.

/* Example */
#include <windows.h>

/* ... */

WCHAR wcs[] = L"안녕하세요.";
CHAR mbs[56] = { '\0', };

WideCharToMultiByte(CP_ACP, 0, wcs, _countof(wcs), mbs, sizeof(mbs), NULL, NULL);

/* ... */

 

다음은 CHAR형 문자열에서 WCHAR형 문자열로 변환하는 예이다.

/* Example */
#include <windows.h>

/* ... */

CHAR mbs[] = "안녕하세요.";
WCHAR wcs[56] = { L'\0', };

MultiByteToWideChar(CP_ACP, 0, mbs, sizeof(mbs), wcs, _countof(wcs));

/* ... */

 

2. OLECHAR, LPOLESTR, LPCOLESTR


이 문자열은 ATLActive Template Library, OLEObject Linking and Embedding에서 정의된 문자열 중 하나이다. Windows 운영체제에서 이 문자열은 항상 WCHAR형 문자열과 같다. 구 Macintosh(현재의 OS X가 아닌 클래식 맥)에서 구동되던 COM/OLE에서는 이 문자열이 CHAR형 Multibyte 문자열과 같았다. 문자열 리터럴 상수를 이 문자열형에 대입하기 위해서는 OLESTR 매크로를 사용한다.

/* Definition */
#ifdef _WIN32
    typedef wchar_t OLECHAR;
    typedef wchar_t * LPOLESTR;
    typedef const wchar_t * LPCOLESTR;
    #define OLESTR(str) L##str
#else
    typedef char OLECHAR;
    typedef char * LPOLESTR;
    typedef const char * LPCOLESTR;
    #define OLESTR(str) str
#else

 

2-1. OLECHAR과 WCHAR 상호변환, OLECHAR과 CHAR 상호변환 OLECHAR과 TCHAR간 상호 변환


atlconv.h 헤더파일을 include하면 OLESTR, WCHAR, TCHAR, CHAR, BSTR간 상호 변환이 가능합니다. BSTR에 대해서는 다음 문단에 이어서 설명하고 여기에서는 OLESTR, WCHAR, TCHAR, CHAR간의 상호 변환만을 사용해보겠다.

OLECHAR에서 WCHAR로 변환하기 위한 함수는 다음과 같다.

  • OLE2W(LPOLESTR에서 LPWSTR)
  • OLE2CW(LPOLESTR에서 LPCWSTR)
  • COLE2W(LPCOLESTR에서 LPWSTR)
  • COLE2CW(LPCOLESTR에서 LPCWSTR)

 

OLESTR에서 CHAR로 변환하기 위한 함수는 다음과 같다.

  • OLE2A(LPOLESTR에서 LPSTR)
  • OLE2CA(LPOLESTR에서 LPCSTR)
  • COLE2A(LPCOLESTR에서 LPSTR)
  • COLE2CA(LPCOLESTR에서 LPCSTR)

 

OLESTR에서 TCHAR로 변환하기 위한 함수는 다음과 같다.

  • OLE2T(LPOLESTR에서 LPTSTR)
  • OLE2CT(LPOLESTR에서 LPCTSTR)
  • COLE2T(LPCOLESTR에서 LPTSTR)
  • COLE2CT(LPCOLESTR에서 LPCTSTR)

 

WCHAR에서 OLECHAR로 변환하기 위한 함수는 다음과 같다.

  • W2OLE(LPWSTR에서 LPOLESTR)
  • W2COLE(LPWSTR에서 LPCOLESTR)
  • CW2OLE(LPCWSTR에서 LPOLESTR)
  • CW2COLE(LPCWSTR에서 LPCOLESTR)

 

CHAR에서 OLECHAR로 변환하기 위한 함수는 다음과 같다.

  • A2OLE(LPSTR에서 LPOLESTR)
  • A2COLE(LPSTR에서 LPCOLESTR)
  • CA2OLE(LPCSTR에서 LPOLESTR)
  • CA2COLE(LPCSTR에서 LPCOLESTR)

 

TCHAR에서 OLECHAR로 변환하기 위한 함수는 다음과 같다.

  • T2OLE(LPTSTR에서 LPOLESTR)
  • T2COLE(LPTSTR에서 LPCOLESTR)
  • CT2OLE(LPCTSTR에서 LPOLESTR)
  • CT2COLE(LPCTSTR에서 LPCOLESTR)

 

위 함수를 사용하여 OLECHAR을 WCHAR, CHAR, TCHAR로 변환해보겠다. 이 함수들은 atlconv.h에 정의되어 있으며 호출 전에 USES_CONVERSION 매크로 호출을 필요로 합니다. 이 매크로는 내부적으로 로케일에 맞춰 리셋하는 것과 관련한 내용이 있다.

/* Example */
#include <windows.h>
#include <atlconv.h>

/* ... */

OLECHAR ocs[] = OLESTR("안녕하세요.");

LPTSTR tcs = NULL;
LPWSTR wcs = NULL;
LPSTR mbs = NULL;

LPCTSTR ctcs = NULL;
LPCWSTR cwcs = NULL;
LPCSTR cmbs = NULL;

USES_CONVERSION;

tcs = OLE2T(ocs);
wcs = OLE2W(ocs);
mbs = OLE2A(ocs);

ctcs = OLE2CT(ocs);
cwcs = OLE2CW(ocs);
cmbs = OLE2CA(ocs);

/* ... */

 

여기서 tcs, wcs, mbs 포인터는 각 변환함수로부터 반환된 포인터를 반환받는데, 이 포인터를 직접 해제할 필요는 없다. 오히려 해제하면 안 된다. 왜냐하면 MSDN이 이 버퍼는 정적 버퍼이므로 해제하지 않는다고 되어 있기 때문이다. 다만, 정적버퍼이기 때문에 너무 1MB를 넘는 긴 문자열을 이 함수를 통해 변환해서는 안될 것이다.

이번에는 이 ATL 매크로 함수를 사용하여 TCHAR, CHAR, WCHAR간 상호 변환에 대해 알아보겠다.

TCHAR과 CHAR간 변환하기 위한 함수는 다음과 같다.

  • T2A(LPTSTR에서 LPSTR)
  • T2CA(LPTSTR에서 LPCSTR)
  • CT2A(LPCTSTR에서 LPSTR)
  • CT2CA(LPCTSTR에서 LPCSTR)
  • A2T(LPSTR에서 LPSTR)
  • A2CT(LPSTR에서 LPCSTR)
  • CA2T(LPCSTR에서 LPSTR)
  • CA2CT(LPCSTR에서 LPCSTR)

 

TCHAR과 WCHAR간 변환하기 위한 함수는 다음과 같다.

  • T2W(LPTSTR에서 LPWSTR)
  • T2CW(LPTSTR에서 LPCWSTR)
  • W2T(LPWSTR에서 LPTSTR)
  • W2CT(LPWSTR에서 LPCTSTR)
  • CT2W(LPCTSTR에서 LPWSTR)
  • CT2CW(LPCTSTR에서 LPCWSTR)
  • CW2T(LPCWSTR에서 LPTSTR)
  • CW2CT(LPCWSTR에서 LPCTSTR)

 

CHAR과 WCHAR간 변환하기 위한 함수는 다음과 같다.

  • A2W(LPSTR에서 LPWSTR)
  • A2CW(LPSTR에서 LPCWSTR)
  • W2A(LPWSTR에서 LPSTR)
  • W2CA(LPWSTR에서 LPCSTR)
  • CA2W(LPCSTR에서 LPWSTR)
  • CA2CW(LPCSTR에서 LPCWSTR)
  • CW2A(LPCWSTR에서 LPSTR)
  • CW2CA(LPCWSTR에서 LPCSTR)

 

3. CString


CString은 MFCMicrosoft Foundation Class에서 사용하는 문자열로서 Multibyte String 또는 Wide String 및 각종 문자열 함수를 래핑한 자료형이다. C++ 클래스로 작성되었으며 생성자를 통해 다양한 형식의 문자열을 받아 내부적으로 자동 변환하므로 사용이 편하다.

다음은 CHAR, WCHAR, TCHAR 문자열을 각각 CString으로 변환하는 예이다.

/* Example */
#include <afxwin.h>

/* ... */

TCHAR tcs[] = TEXT("피자");
WCHAR wcs[] = L"햄버거";
CHAR mbs[] = "스파게티";

CString cst1 = CString(tcs);
CString cst2 = CString(wcs);
CString cst3 = CString(mbs);

/* ... */

 

CString에서 포인터 문자열로의 변환은 GetBuffer 함수로 수행한다. 이 때 반환형이 TCHAR임에 주의하기 바라며, 사용 후에는 반드시 ReleaseBuffer를 호출한다.

/* Example */
#include <afxwin.h>

/* ... */	

CString cst = CString(TEXT("안녕하세요."));
LPTSTR tcs = NULL;

tcs = cst.GetBuffer();

// ... 포인터 문자열에 대한 작업

cst.ReleaseBuffer();

/* ... */

 

3. 마무리

여기까지 해서 Windows API 및 ATL 문자열에 대해 설명하였다. 다음 포스트에서는 COM 문자열에 대해 설명하겠다.

 

 

카테고리 “Microsoft Windows/Win32 문자열 처리”
more...

“분류 전체보기” (134건)