我正在寫一個程序員用戶友好的inetd樣式包裝器,但都在C中。這個守護進程會一直運行,必須快速,所以inetd不是一種選擇。此外,我希望下面的代碼對其他人有用 - 事實上,作爲新手,我發現它已經不存在很多次了。 (我寫我的代碼,因爲我剛纔的問題,這是否存在某些庫違反準則。)C tcp socket magic:clientip,關閉端口,重新連接標準輸入/標準輸出,sigaction,速度
我有一對夫婦的遺留問題:
我不知道如何獲得在XXX的clientip。 xxx.xxx.xxx表單。在tcpserver_clientip()中,這需要寫入c-> ipaddress,然後由我的tcpserver_clientip()函數返回。我也很想知道如何獲得客戶端DNS名稱tcpserver_clientname()的反向查找,但這是另一天。
我不知道如何關閉本地端口,以便在有序程序終止時立即釋放。現在,端口在幾秒鐘後再次可用。依靠進程死亡釋放資源似乎對我來說是一個錯誤。 setsockopt也看起來很奇怪 - 我想在有效的退出時關閉它,而不是在打開它時設置一個選項。
我寧願如果,而不是使用tcpserver_clientwrite(),偵聽器()可以打印到標準輸出;而不是使用tcpserver_clientread(),listener()可以從標準輸入讀取。這是可能的在Linux(和C)沒有損失大量的速度?我不相信。也就是說,我不相信可以將stdin/stdout掛接到套接字recvfrom和sendto。而不是猜測,我想問。
沒有人知道爲什麼sigaction()失敗,其中signal()的作用? (爲了保護我免受殭屍兒童的侵害)這是在linux下的gcc。 sigaction和信號的原型似乎有所不同。
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;
}
我的代碼是弗蘭肯代碼---我的一些,一些修補在一起。這個想法是將硬件部分封裝起來,這樣我的用戶就可以輕鬆完成這個簡單的部分。我對套接字和多處理很陌生。在它上面工作---我需要輕鬆。感謝指針。唉,當你說「不失速度......」時,你能舉個例子嗎?我怎麼能這樣做,讓聽衆可以讀取stdin而不是調用tcpserver_read()通過recv獲取消息? –
@ivoWelch您如何看待這將改變速度? – EJP