본문 바로가기

TroubleShooting/Network

unix socket 을 이용하는 예제

728x90
 유닉스 소켓을 이용해서 동일 서버의 프로그램과 통신할 일이 있다. 사용 예제를 정리해본다.
 

/*
Sample userspace kHTTPd logging-daemon
Note:
This logging-daemon is only intended to serve as an example on how to
communicate with the kHTTPd logging system. This daemon is by no means
intended for real usage and is not secure enough for usage on systems
with "hostile" users. This might change in the future though.
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

#include "khttpd_log.h"

int SocketFD;

int AcceptSocket()
{
struct sockaddr_un  name;
int sock;
size_t size;
remove("/tmp/.khttpdlog");
sock = socket(PF_UNIX,SOCK_STREAM,0);
name.sun_family = AF_FILE;
strcpy(name.sun_path,"/tmp/.khttpdlog");
size = (offsetof(struct sockaddr_un,sun_path) + strlen(name.sun_path)+1);
if (bind(sock,(struct sockaddr *)&name,size) < 0)
{
fprintf(stderr,"Error binding \n");
exit(0);
}
listen(sock,2);

return accept(sock,NULL,NULL);
}

int main()
{
int Socket;
int iter=0;
int len;
struct tm *TM;
FILE *file;
char *Time;
struct khttpd_loginfo Req;
printf("Opening socket \n");
Socket = AcceptSocket();
printf("Opening Logfile \n");
file = fopen("/var/log/khttpd.log","w");
while (1==1)
{
len = recv(Socket,&Req,sizeof(Req),0);
if (len<=0) break;
      
TM = gmtime(&Req.Datestamp);
Time = asctime(TM);
fprintf(file,"%08x:\t%s\t%i\t%i\%s \n",Req.RemoteHost,Time,Req.Status,Req.Filesize,Req.Filename);
/* printf("Filename = %s \n",Req.Filename);
printf("Time = %s \n",Time);
*/
iter++;
/* if (iter&15)
fflush(file);*/
}
fclose(file);
return 0;
}

 khttpdlogd 라는 프로그램의 일부 소스이다.khttpd 는 커널과 userspace 간 복사에 드는 비용을 줄이고자, 커널에서 처리하도록 하는 웹서버이다. khttpdlogd 는 khttpd와 통신하여 로그(/var/log/khttpd.log)를 남기는 목적인듯하다.

 AcceptSocket() 함수에서 struct sockaddr_un 가 나오는데, size 를 계산하는 부분이 특이하다.
  size = (offsetof(struct sockaddr_un,sun_path) + strlen(name.sun_path)+1);
 
 OFFSETOF(3) 이라는 매크로를 사용하여 sun_path 이전까지의 길이값을 얻어오고 있다. offsetof() 메뉴얼을 확인해보면, sa_family_t 의 size 가 compiler 또는 padding 마다 틀려지는 상황에서 아주 유용하다고 나온다.
 struct sockaddr_un {
     sa_family_t    sun_family;            // 주소 패밀리
     char             sun_path[108];      // 경로 이름
}

 또한 리눅스에서 /usr/include/sys/un.h 를 확인해보면, sockaddr_un 구조체가 선언되어 있고 아래에 계산식인 SUN_LEN 이 define 이 나온다. offsetof 매크로와 동일한 기능을 수행하는 듯하다.

 /* Structure describing the address of an AF_LOCAL (aka AF_UNIX) socket.  */
struct sockaddr_un
  {
    __SOCKADDR_COMMON (sun_);
    char sun_path[108];         /* Path name.  */
  };

/* Evaluate to actual length of the `sockaddr_un' structure.  */
# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
                      + strlen ((ptr)->sun_path))

 소스를 잘 살펴보면, sockaddr_un 구조체의 sun_path 까지의 길이와 sun_path 문자열의 길이를 합하고 있다. 
 (size_t) (((struct sockaddr_un *) 0)->sun_path) 부분이 처음보는 코드라 이해가 안되었지만, 분리해서 보면 (size_t) 와 (sun_path) 의 포인터이다. 결국 sockaddr_un 구조체의 sun_path 위치까지의 길이(size_t) 를 나타내는 것이다. (참고로, (size_t)(0) 의 값은 0 이고, (size_t)(1) 의 값은 1 이다.)

PS.

 "UNIX networking programming (저자: W. Richard Stevens)" 에서 유닉스 소켓관련한 코드에서도 동일한 방법으로 처리하고 있다. 1990년에 집필되었으니, 그 이후로 많은 프로그램 코드들에 영향을 끼쳤을테다.
 servlen = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path);

 그런데, "예제로 배우는 리눅스 소켓 프로그래밍(저자: 워런 W. 게이)" 에서 유닉스 소켓 관련 코드를 보았는데 아무리 예제지만 해당 부분이 이상하게 처리가 되었고 설명하는 것을 건너 띄고 있었다.
관련 자료 : P509, 17장 자격증명과 파일 설명자 전달하기

'TroubleShooting > Network' 카테고리의 다른 글

ftp 정리  (0) 2010.11.05
snort inline 정리.... 시작  (0) 2010.08.25
linux 에서 fd_set MAX 변경  (0) 2010.06.14
채팅 서버 - epoll 버전  (0) 2010.06.14
채팅 서버(윈도우용) - select 버전  (0) 2010.06.14