2013-10-13 56 views
0

我正在寫一個程序員用戶友好的inetd樣式包裝器,但都在C中。這個守護進程會一直運行,必須快速,所以inetd不是一種選擇。此外,我希望下面的代碼對其他人有用 - 事實上,作爲新手,我發現它已經不存在很多次了。 (我寫我的代碼,因爲我剛纔的問題,這是否存在某些庫違反準則。)C tcp socket magic:clientip,關閉端口,重新連接標準輸入/標準輸出,sigaction,速度

我有一對夫婦的遺留問題:

  1. 我不知道如何獲得在XXX的clientip。 xxx.xxx.xxx表單。在tcpserver_clientip()中,這需要寫入c-> ipaddress,然後由我的tcpserver_clientip()函數返回。我也很想知道如何獲得客戶端DNS名稱tcpserver_clientname()的反向查找,但這是另一天。

  2. 我不知道如何關閉本地端口,以便在有序程序終止時立即釋放。現在,端口在幾秒鐘後再次可用。依靠進程死亡釋放資源似乎對我來說是一個錯誤。 setsockopt也看起來很奇怪 - 我想在有效的退出時關閉它,而不是在打開它時設置一個選項。

  3. 我寧願如果,而不是使用tcpserver_clientwrite(),偵聽器()可以打印到標準輸出;而不是使用tcpserver_clientread(),listener()可以從標準輸入讀取。這是可能的在Linux(和C)沒有損失大量的速度?我不相信。也就是說,我不相信可以將stdin/stdout掛接到套接字recvfrom和sendto。而不是猜測,我想問。

  4. 沒有人知道爲什麼sigaction()失敗,其中signal()的作用? (爲了保護我免受殭屍兒童的侵害)這是在linux下的gcc。 sigaction和信號的原型似乎有所不同。

  5. for tcp(not udp),有沒有一個這樣的示例程序,工作沒有分叉?我只用叉子看過這種類型的代碼。據推測,讀者/寫作者的功能必須具有一種結構,以便特定進出的許多客戶中的哪一個來自/將要進入。大概,避免叉可以節省更多的時間。

此外,如果有人注意到安全漏洞或其他錯誤,請讓我知道。

因此,這裏是我的佈局及用途:

/* 

NAME 

    tcpserver.h 

DESCRIPTION 

    a high-level library implementation of a tcp server, with an inetd-like 
    ease-of-use. the library takes care of forking, reaping, etc. The focus 
    right now are clean text connections ('\0' terminated, but overflow safe). 

BUGS 

USER INTERFACE 

    the user calls the tcpserver function with a callback, the listener(): 
*/ 

static int tcpserver(const int port, const int msgmaxlen, int (*listener)(void *)); 
static int tcpserver_verbose(const int port, const int msgmaxlen, int (*listener)(void *)); 

/* 
    the listener() has access to the following functions: 
*/ 

static int tcpserver_clientwrite(const void *, const char *msg); 
static char *tcpserver_clientread(const void *); 
static char *tcpserver_clientip(void *); // this will store inside the structure, so its volatile 
static int tcpserver_clientport(const void *); 

static const char *tcpserver_version= "tcpserver.h 0.1"; 

這裏是我的我要如何使用它例如:

USE EXAMPLE 

#include <stdio.h> 
#include "tcpserver.h" 

int listener(void *c) { 
    printf("listener %d> Hello IP='%s' on port %d\n", getpid(), 
    tcpserver_clientip(c), tcpserver_clientport(c)); 

    int terminate=0; 
    do { 
    char *msgin= tcpserver_clientread(c); 
    printf("listener %d r> '%s'\n", getpid(), msgin); 
    tcpserver_clientwrite(c, msgin); 

    printf("listener %d w> '%s'\n", getpid(), msgin); 
    fflush(stdout); 
    terminate= (((strncmp(msgin, "quit", 4)==0)&&(msgin[4]=='\r')) 
     ||((strncmp(msgin, "bye", 3)==0)&&(msgin[3]=='\r'))); 
    } while (!terminate); 

    printf("\nlistener %d> **** listener requested orderly termination ****\n", getpid()); 

    exit(EXIT_SUCCESS); 
} 

int main(int argc, char *argv[]) { 
    const int PORT= (argc>=2) ? atoi(argv[1]) : (32001); 
    const int MAXMSGLEN=1024; 
    printf("Parent %d. version %s\n", getpid(), tcpserver_version); 
    return tcpserver(PORT, MAXMSGLEN, &listener); 
} 

,這裏是代碼本身,還塞進同.h文件中:

/****************************************************************/ 

#ifndef INETVERBOSE 
#define INETVERBOSE 1 
#endif 

#include <stdlib.h> 
#include <strings.h> 
#include <string.h> 
#include <signal.h> 
#include <stdio.h> 
#include <unistd.h> 

#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <sys/wait.h> 
#include <sys/socket.h> 

/******************************************************************************************************************************** 
* Storage for each client connection 
****************************************************************/ 

#define MAXIPSTRLEN 

struct tcpserver_clientconnection { 
    struct sockaddr_in cliaddr; // Socket Library 
    int connfd; // Socket Library 
    socklen_t clilen; // Socket Library 

    // read/write to client 
    char *msgbuf; // the buffer for a single longest message 
    int msgmaxlen; // its maximum length 
    int verbose; // makes debugging easier 

    // write by tcpserver, read-only to client 
    unsigned int port; 
    char ipaddress[MAXIPSTRLEN]; 
}; 



/******************************************************************************************************************************** 
* The Actual Server 
****************************************************************/ 

static int _tcpserver(const int port, const int msgmaxlen, int (*listener)(void *c), const int verbose) { 

    void die(const char *s) { fprintf(stderr, "tcpserver.h death: %s : ", s); perror(""); exit(EXIT_FAILURE); } 

    const int parentpid= getpid(); 
    const int listenfd=socket(AF_INET,SOCK_STREAM,0); 

    struct sockaddr_in servaddr; 

    bzero(&servaddr,sizeof(servaddr)); 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY); 
    servaddr.sin_port=htons(port); 

    if (verbose) fprintf(stderr, "[parent %d] ---------------- Listening on Port %d\n", parentpid, port); 
    if (bind(listenfd,(struct sockaddr *)&(servaddr),sizeof(servaddr))) 
    die("Sorry, we could not bind to the socket"); 

    // safety --- reap all children 
    signal(SIGCHLD, SIG_IGN); // sigaction(SIGCHLD, SA_NOCLDWAIT) 

    if (listen(listenfd,1024)) die("Cannot listen to you."); 

    for(;;) { 
    // collect what we need to pass 
    struct tcpserver_clientconnection c; 
    c.port= port; 

    c.clilen=sizeof(c.cliaddr); 
    c.connfd = accept(listenfd,(struct sockaddr *)&(c.cliaddr),&(c.clilen)); 

    if (verbose) fprintf(stderr, "[parent %d] incoming connection ", parentpid); 

    pid_t childpid; 
    if ((childpid = fork()) == 0) { 
     close(listenfd); // the child can close its listenfd connection 

     if (verbose) { fprintf(stderr, "[Spawned Child Process %d for connection]\n", getpid()); } 
     c.verbose= verbose; 
     c.msgmaxlen= msgmaxlen; 
     c.msgbuf= (char *) calloc(msgmaxlen, sizeof(char)); 
     if (!c.msgbuf) die("cannot allocate memory to message buffer"); 
     listener(&c); 
     free(c.msgbuf); 
    } 

    int status; 
    waitpid(childpid, &status, 0); 
    if (verbose) fprintf(stderr, "[Parent Asynch: Child %d was stopped with return value %d]\n", childpid, status); 
    close(c.connfd); // release the connection 
    } 
    if (verbose) fprintf(stderr, "[%d] Main Program Bye", parentpid); 
    return EXIT_SUCCESS; 
} 


static inline int tcpserver_verbose(const int port, const int msgmaxlen, int (*listener)(void *c)) { 
    return _tcpserver(port, msgmaxlen, listener, 1); 
} 
static inline int tcpserver(const int port, const int msgmaxlen, int (*listener)(void *c)) { 
    return _tcpserver(port, msgmaxlen, listener, 0); 
} 


/******************************************************************************************************************************** 
* Listener (Client) Access Functions 
****************************************************************/ 
static char *tcpserver_clientip(void *v) { 
    struct tcpserver_clientconnection *c= (struct tcpserver_clientconnection *)v; 

    const char *peer_addr(int sockfd, char *ipnamedest, size_t ipnamedestsiz) { 
    struct sockaddr_in adr_inet; 
    unsigned int len_inet = sizeof(adr_inet); 
    int z = getpeername(sockfd, (struct sockaddr *)&adr_inet, &len_inet); 
    if (z == -1) return NULL; /* Failed */ 
    z = snprintf(ipnamedest, ipnamedestsiz, "%s:%d", inet_ntoa(adr_inet.sin_addr), (unsigned)ntohs(adr_inet.sin_port)); 
    if (z == -1) return NULL; /* Ipnamedestfer too small */ 
    return ipnamedest; 
    } 

    strcpy(c->ipaddress, "unknown"); 
    //peer_addr(listenfd, c->ipaddress, MAXIPSTRLEN); 
    if (c->verbose) fprintf(stderr, "IP=%s\n", c->ipaddress); 

    return c->ipaddress; 
} 

static int tcpserver_clientport(const void *v) { 
    struct tcpserver_clientconnection *c= (struct tcpserver_clientconnection *)v; 
    return c->port; 
} 

static char *tcpserver_clientread(const void *v) { 
    struct tcpserver_clientconnection *c= (struct tcpserver_clientconnection *)v; 
    if (!c->msgbuf) perror("internal error: NULL msgbuf"); 
    int rc= recvfrom(c->connfd, c->msgbuf, c->msgmaxlen, 0, (struct sockaddr *)&(c->cliaddr), &(c->clilen)); 
    if (rc<0) { perror("what is wrong with you?\n"); return NULL; } 
    c->msgbuf[c->msgmaxlen -1]='\0'; // for safety...always 
    if (rc>0) c->msgbuf[rc]='\0'; // for safety...always 
    if (c->verbose) fprintf(stderr, "clientread> '%s'\n", c->msgbuf); 
    return c->msgbuf; 
} 

static int tcpserver_clientwrite(const void *v, const char *msg) { 
    struct tcpserver_clientconnection *c= (struct tcpserver_clientconnection *)v; 

    if (!msg) perror("why would you want to write a NULL msg??"); 
    int n=strlen(msg); 
    if (n==(-1)) n= strlen(c->msgbuf); // -1 means textmode 
    if (n > (c->msgmaxlen)) n= c->msgmaxlen; 
    if (c->verbose) fprintf(stderr, "clientwriting> '%s'\n", c->msgbuf); 
    sendto(c->connfd, c->msgbuf, n, 0, (struct sockaddr *)&(c->cliaddr),sizeof((c->cliaddr))); 
    if (c->verbose) fprintf(stderr, "clientwritten> '%s'\n", c->msgbuf); 
    return n; 
} 

回答

2

我不知道如何獲得在xxx.xxx.xxx.xxx格式中的clientip。

你不需要那種形式。你需要它的二進制形式,作爲sockaddr_in。其他任何東西都只是美容。

我不知道如何關閉本地端口

隨着close()方法。

我寧願如果,而不是使用tcpserver_clientwrite(),listener()可以打印到標準輸出;而不是使用tcpserver_clientread(),listener()可以從標準輸入讀取。這是可能的Linux(和C)

是的。

沒有失去很大的速度?

不失任何速度。我不知道你爲什麼不這樣想。並不是說這是個好主意。

你的代碼並不像你想象的那麼棒。例如,它一次只處理一個客戶端,而不是並行處理它們,因爲您在啓動後等待每個小孩。您還在等孩子這是一個bug。您的消息發送/接收API對send()和recv()沒有任何改進。

+0

我的代碼是弗蘭肯代碼---我的一些,一些修補在一起。這個想法是將硬件部分封裝起來,這樣我的用戶就可以輕鬆完成這個簡單的部分。我對套接字和多處理很陌生。在它上面工作---我需要輕鬆。感謝指針。唉,當你說「不失速度......」時,你能舉個例子嗎?我怎麼能這樣做,讓聽衆可以讀取stdin而不是調用tcpserver_read()通過recv獲取消息? –

+0

@ivoWelch您如何看待這將改變速度? – EJP

1

我該如何讓聽衆可以讀取stdin而不是調用 tcpserver_read()通過recv獲取消息?

您可以像inetd一樣將作爲標準輸入和標準輸出傳遞到服務器。

   dup2(c->connfd, 0); 
      close(c->connfd); 
      dup2(0, 1); 
相關問題