BSD Socket 사용법 정리
FreeBSD(+*nix) 버전 클라이언트측 코드
본 포스팅에서는 FreeBSD를 포함하여 *nix를 기준으로 한 IPC 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 <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 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);
fdserver = socket(AF_UNIX, SOCK_STREAM, 0);
if (fdserver < 0) {
printf("[FAILURE] socket(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
pthread_exit((void *)((intptr_t)errno));
}
printf("[SUCCESS] socket() @ pthread_t = %p\n", thself);
cbaddrserver = sizeof(struct sockaddr_un);
bzero(&addrserver, cbaddrserver);
addrserver.sun_family = AF_UNIX;
strncpy(addrserver.sun_path, SCK_FILE, (sizeof(addrserver.sun_path) / sizeof(addrserver.sun_path[1])));
sockresult = connect(fdserver, (struct sockaddr *)&addrserver, cbaddrserver);
if (sockresult < 0) {
printf("[FAILURE] connect(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
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) {
printf("[FAILURE] read(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
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) {
printf("[FAILURE] write(): %d(%s) @ pthread_t = %p\n", errno, strerror(errno), thself);
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)0));
pthread_cleanup_pop(0);
return 0;
}
void cleanup_routine(void * pfdsocket) {
pthread_t thself;
int fdsocket;
int sockresult;
fdsocket = *((int *)pfdsocket);
thself = pthread_self();
if (fdsocket) {
sockresult = close(fdsocket);
if (sockresult < 0) {
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
함수를 호출하는 부분에서 '정의되지 않은 식별자' 오류를 낼 것이다.
서버 접속에 성공하면 다음과 같이 서버로부터 메시지를 수신한다. 여기서 0x800681500
은 이 클라이언트와 통신하고 있는 서버 측 thread ID이다.