2010-07-21 4 views
1

처리를 위해 작업자 프로세스에 소켓 설명자를 전달하는 서버를 구현하고 있습니다. sendmsg 시스템 호출을 사용하여 UNIX 소켓을 통해 메시지를 보냅니다. 서버 자체는 UNIX 소켓 또는 INET 소켓에서 수신 대기 할 수 있습니다. 나는 nginx 뒤에 그것을두고 문제가 있습니다. 내 서버가 INET 소켓에서 작동하면 모든 것이 정상입니다. 그러나 유닉스 소켓을 통한 프록 싱은 작동하지 않습니다. 여기Nginx 프록시 모듈 및 소켓 설명자 전달

import sys 
import socket 
sock = socket.socket(socket.AF_UNIX) 
sock.connect(sys.argv[1]) 
while 1: 
    data = sock.recv(1024) 
    if not data: break 
    print data 

을 최소한의 예입니다

readv() failed (104: Connection reset by peer) while reading upstream 

간단한 파이썬 스크립트는 문제없이 데이터를 수신 : nginx를보고합니다. 주석 처리 된 두 줄의 주석을 제거하면 문제가 해결됩니다.

#include <time.h> 
#include <sys/socket.h> 
#include <sys/un.h> 

const char response[] = 
    "HTTP/1.0 200 OK\r\n" 
    "Content-Type: text/plain\r\n" 
    "Content-Length: 4\r\n" 
    "\r\n" 
    "test"; 

int main(int argc, char** argv) 
{ 
    struct msghdr msg; 
    msg.msg_name = 0; 
    msg.msg_namelen = 0; 
    struct iovec iov; 
    char c; 
    iov.iov_base = &c; 
    iov.iov_len = 1; 
    msg.msg_iov = &iov; 
    msg.msg_iovlen = 1; 
    char control[CMSG_SPACE(sizeof(int))]; 
    msg.msg_control = control; 
    msg.msg_controllen = sizeof(control); 
    struct cmsghdr* cmsg_ptr = CMSG_FIRSTHDR(&msg); 
    int pair[2]; 
    socketpair(AF_UNIX, SOCK_STREAM, 0, pair); 
    if (fork()) { 
     close(pair[1]); 
     cmsg_ptr->cmsg_level = SOL_SOCKET; 
     cmsg_ptr->cmsg_type = SCM_RIGHTS; 
     cmsg_ptr->cmsg_len = CMSG_LEN(sizeof(int)); 
     int listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); 
     unlink(argv[1]); 
     struct sockaddr_un address; 
     address.sun_family = AF_UNIX; 
     strncpy(address.sun_path, argv[1], sizeof(address.sun_path) - 1); 
     bind(listen_fd, (struct sockaddr*)(&address), SUN_LEN(&address)); 
     chmod(argv[1], 0666); 
     listen(listen_fd, SOMAXCONN); 
     for (;;) { 
      int conn_fd = accept(listen_fd, 0, 0); 
      *(int*)(CMSG_DATA(cmsg_ptr)) = conn_fd; 
      sendmsg(pair[0], &msg, 0); 
/*    sleep(1); */ 
      close(conn_fd); 
     } 
    } else { 
     close(pair[0]); 
     for (;;) { 
      recvmsg(pair[1], &msg, 0); 
      int conn_fd = *(int*)(CMSG_DATA(cmsg_ptr)); 
      write(conn_fd, response, sizeof(response)); 
/*    shutdown(conn_fd, SHUT_WR); */ 
      close(conn_fd); 
     } 
    } 
    return 0; 
} 

저는 리눅스에서 nginx/0.7.62를 사용합니다. 나는 nginx를 파헤쳐야 하나, 아니면 descriptor passing을 오해하니?

답변

1

sleep()은 필요하지 않습니다. shutdown()이 맞습니다.

write(conn_fd, response, ...); 

/* Tell client no more data is forthcoming */ 
shutdown(conn_fd, SHUT_WR); 

/* Read until client also closes */ 
while (read(conn_fd, buffer, sizeof buffer) > 0) 
{ 
    /* ... */ 
} 

/* Now we can close */ 
close(conn_fd); 

은 "피어에 의해 연결 재설정"응용 프로그램이 전에 소켓을 닫은 OS 알리는의 nginx입니다 : 수정은의 nginx에서 파일의 끝을 볼 때까지 당신의 자식 프로세스가 conn_fd을 닫지해야한다는 것입니다 nginx가 보낸 모든 것을 보았습니다. "모든 것"은 데이터 바이트가 아니라 스트림의 논리적 인 끝을 포함합니다.

+0

고마워요! 완벽하게 작동 :) –