首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C tcp套接字魔术: clientip,关闭端口,重新挂钩stdin/stdout,σ动作,速度

C tcp套接字魔术: clientip,关闭端口,重新挂钩stdin/stdout,σ动作,速度
EN

Stack Overflow用户
提问于 2013-10-13 06:38:51
回答 2查看 381关注 0票数 0

我正在编写一个程序员友好的inetd风格的包装器,但是在C.这个守护进程将一直运行,而且它必须是快速的,所以inetd不是一个选项。此外,我希望下面的代码能对其他人有用--事实上,作为新手,我觉得奇怪的是,它不存在很多次了。(我编写代码是因为我先前提出的问题是否存在于某些库中,这违反了准则。)

我还有几个问题:

  1. 我不知道如何获得xxx.xxx形式的客户端。在tcpserver_clientip()中,这需要写入c->ipaddress,然后由我的tcpserver_clientip()函数返回。我还想知道如何反向查找客户端DNS名称tcpserver_clientname(),但这是另一天的事情。
  2. 我不知道如何关闭本地端口,以便在有秩序的程序终止时立即释放。现在,几秒钟后端口再次可用。对我来说,依赖进程死亡来释放资源似乎是一个bug。setsockopt做这个似乎也很奇怪--我想在有效的出口时关闭它,而不是在打开它时设置一个选项。
  3. 我更希望监听器()可以打印到stdout,而不是使用tcpserver_clientwrite();监听器()可以从stdin读取,而不是使用tcpserver_clientread()。在linux (和C)中,这有可能在不损失大量速度的情况下实现吗?我不相信是这样。也就是说,我不相信一个人可以将stdin/stdout钩到套接字中,从和sendto。但我不想猜测,我想问。
  4. 有人知道sigaction()为什么会失败,信号()在哪里工作吗?(为了保护我不受僵尸孩子的伤害?)这是linux下的gcc。信号和信号的原型似乎是不同的。
  5. 对于tcp (而不是udp),是否有这样的示例程序可以不分叉地工作?我只看过这种带分叉的代码。想必,读取器/撰稿人函数必须带有一个结构,说明某个特定的客户机/输出来自/将要到达哪个客户端。而且,避免使用叉子可能会节省更多时间。

另外,如果有人注意到安全漏洞或其他错误,请告诉我。

以下是我的布局和目的:

代码语言:javascript
复制
/*

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";

下面是我想如何使用它的例子:

代码语言:javascript
复制
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文件中:

代码语言:javascript
复制
/****************************************************************/

#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;
}
EN

回答 2

Stack Overflow用户

发布于 2013-10-13 06:50:15

我不知道如何获得xxx.xxx形式的客户端。

你不需要那种形式的。您需要它的二进制形式,作为一个sockaddr_in。其他的一切都只是化妆品。

我不知道如何关闭本地港口。

靠得很近。

我更希望监听器()可以打印到stdout,而不是使用tcpserver_clientwrite();监听器()可以从stdin读取,而不是使用tcpserver_clientread()。这在linux (和C)中是可能的吗?

是。

而不会失去很大的速度?

不会失去任何速度。我不知道你为什么不这么想。这不是个好主意。

您的代码并不像您想象的那么出色。例如,它一次只处理一个客户端,而不是并行地处理它们,因为您在启动它之后等待每个子客户机。您也在等待孩子的孩子,这是一个bug。您的消息发送/接收API与send()和recv()相比没有任何改进。

票数 2
EN

Stack Overflow用户

发布于 2014-09-23 09:59:06

如何使侦听器能够读取stdin而不是调用tcpserver_read()来通过recv获取消息?

您可以像inetd一样,将连接的套接字作为stdin和stdout传递给服务器。

代码语言:javascript
复制
             dup2(c->connfd, 0);
             close(c->connfd);
             dup2(0, 1);
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19342588

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档