通过使用popen()的套接字执行命令

任何人都可以试图实现以下服务器和客户端吗?

服务器:

#include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> int main(void) { int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv_addr = { 0 }; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(1234); bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); listen(sock, 128); struct sockaddr_in cli_addr = { 0 }; socklen_t cli_addrlen = sizeof(cli_addr); int acc_sock = accept(sock, (struct sockaddr *)&cli_addr, &cli_addrlen); printf("[+] Connected \n"); char buf[1024]; ssize_t nread; memset(buf, 0, sizeof(buf)); int a; while (1) { nread = read(0, buf, 1024); write(acc_sock, buf, nread); memset(buf, 0, sizeof(buf)); while ((read(acc_sock, buf, 1024)) != 0) { printf("%s", buf); memset(buf, 0, sizeof(buf)); } } } 

所有的服务器都是从stdin扫描命令并通过套接字发送给客户端。 然后扫描客户端响应并将其输出到stdout

客户端:

 #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> int main(int argc, const char *argv[]) { int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv = { 0 }; char buf[1024]; char command[1024]; memset(buf, 0, sizeof(buf)); memset(command, 0, sizeof(command)); int nread; FILE *in; extern FILE *popen(); serv.sin_family = AF_INET; serv.sin_port = htons(atoi(argv[1])); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); int a; connect(sock, (struct sockaddr*)&serv, sizeof(serv)); while (1) { nread = read(sock, buf, 1024); in = popen(buf, "r"); while ((fgets(command, sizeof(command), in)) != NULL) {; write(sock, command, sizeof(command)); } memset(buf, 0, sizeof(buf)); } return 0; } 

本质上,客户端接收服务器扫描的命令,使用popen()执行命令,并逐行发送命令行的内容,直到NULL

问题是代码在第一个命令后突然停止工作。 命令的传输和命令的输出是令人满意的,但是在打印第一个命令的输出之后,程序刚刚停止工作。 我认为是fgets()的问题,但是我可能是错的。 有没有解决这个问题的方法?

Solutions Collecting From Web of "通过使用popen()的套接字执行命令"

警告:这可能[或者可能不]适合您的需求,因为我颠倒了下面更正的代码中的客户端和服务器循环的意义。 正如我在上面的评论中提到的那样:

像这样的应用程序的正常方向是,客户端连接到服务器,客户端将命令[从stdin读取]提供给服务器[ popen ]并反馈结果。 这就是ssh工作原理。 你的方向是颠倒的。 你得到的是你发送sshd并等待ssh连接,然后sshd发送命令给ssh 换句话说,应该切换相应侧面的环路。

扭转这是事情对我有意义的唯一方法。 如果逆转不能正常工作,那么下面的代码可能仍然会给你一些想法。

我通过引入标志字符的概念来表示结束输出来解决挂起问题。 我借用RS-232PPP [点对点]协议借鉴了这个概念。

标志字符只是一个给定的值(例如0x10 ),不可能是正常数据的一部分。 由于你的数据很可能是asciiutf-8 ,所以可以使用0x00-0x1F范围内的任何[unused]字符(即不要使用tab,cr,newline等)。

如果您需要传输标志字符(即您的数据必须是完整的二进制范围0x00-0xFF ),我已经包含了一些数据包编码/解码例程,实现上面PPP使用的转义码。 我编写了它们,但是并没有实际挂钩它们。在这种情况下,flag [和escape]字符可以是任何二进制值[通常分别为0xFF0xFE ]。

为了简单起见,我将双方合并成一个.c文件。 用-s [first]调用服务器。

无论如何,这是测试代码[请原谅免费式的清理]:

 // inetpair/inetpair -- server/client communication #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> typedef unsigned char byte; #define BUFMAX 1024 int port = 1234; int opt_svr; int opt_debug; #define FLAG 0x10 #define ESC 0x11 #define ESC_FLAG 0x01 #define ESC_ESC 0x02 #define dbgprt(_fmt...) \ do { \ if (opt_debug) \ printf(_fmt); \ } while (0) // client int client(void) { int sock; struct sockaddr_in serv = { 0 }; char *cp; char buf[BUFMAX + 1]; int nread; int flag; int exitflg; sock = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(port); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(sock, (struct sockaddr *) &serv, sizeof(serv)); while (1) { cp = fgets(buf,BUFMAX,stdin); if (cp == NULL) break; exitflg = (strcmp(buf,"exit\n") == 0); // send the command nread = strlen(buf); write(sock, buf, nread); if (exitflg) break; while (1) { dbgprt("client: PREREAD\n"); nread = read(sock, buf, 1024); dbgprt("client: POSTREAD nread=%d\n",nread); if (nread <= 0) break; cp = memchr(buf,FLAG,nread); flag = (cp != NULL); if (flag) nread = cp - buf; write(1,buf,nread); if (flag) break; } } close(sock); return 0; } // server int server(void) { struct sockaddr_in serv_addr = { 0 }; int sock; int acc_sock; char buf[BUFMAX + 1]; char command[BUFMAX + 1]; ssize_t nread; FILE *pin; FILE *xfin; char *cp; struct sockaddr_in cli_addr = { 0 }; opt_debug = ! opt_debug; dbgprt("[+] Starting\n"); sock = socket(AF_INET, SOCK_STREAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(port); bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); listen(sock, 128); while (1) { socklen_t cli_addrlen = sizeof(cli_addr); dbgprt("[+] Waiting for connection\n"); acc_sock = accept(sock,(struct sockaddr *)&cli_addr,&cli_addrlen); dbgprt("[+] Connected\n"); xfin = fdopen(acc_sock,"r"); while (1) { dbgprt("[+] Waiting for command\n"); cp = fgets(buf,BUFMAX,xfin); if (cp == NULL) break; cp = strchr(buf,'\n'); if (cp != NULL) *cp = 0; dbgprt("[+] Command '%s'\n",buf); if (strcmp(buf,"exit") == 0) break; pin = popen(buf, "r"); while (1) { cp = fgets(command, BUFMAX, pin); if (cp == NULL) break; nread = strlen(command); write(acc_sock, command, nread); } pclose(pin); command[0] = FLAG; write(acc_sock,command,1); } fclose(xfin); close(acc_sock); dbgprt("[+] Disconnect\n"); } } // packet_encode -- encode packet // RETURNS: (outlen << 1) int packet_encode(void *dst,const void *src,int srclen) { const byte *sp = src; byte *dp = dst; const byte *ep; byte chr; int dstlen; // encode packet in manner similar to PPP (point-to-point) protocol does // over RS-232 line ep = sp + srclen; for (; sp < ep; ++sp) { chr = *sp; switch (chr) { case FLAG: *dp++ = ESC; *dp++ = ESC_FLAG; break; case ESC: *dp++ = ESC; *dp++ = ESC_ESC; break; default: *dp++ = chr; break; } } dstlen = dp - (byte *) dst; dstlen <<= 1; return dstlen; } // packet_decode -- decode packet // RETURNS: (outlen << 1) | flag int packet_decode(void *dst,const void *src,int srclen) { const byte *sp = src; byte *dp = dst; const byte *ep; byte chr; int flag; int dstlen; // decode packet in manner similar to PPP (point-to-point) protocol does // over RS-232 line ep = sp + srclen; flag = 0; while (sp < ep) { chr = *sp++; flag = (chr == FLAG); if (flag) break; switch (chr) { case ESC: chr = *sp++; switch (chr) { case ESC_FLAG: *dp++ = FLAG; break; case ESC_ESC: *dp++ = ESC; break; } break; default: *dp++ = chr; break; } } dstlen = dp - (byte *) dst; dstlen <<= 1; if (flag) dstlen |= 0x01; return dstlen; } int main(int argc, char **argv) { char *cp; --argc; ++argv; for (; argc > 0; --argc, ++argv) { cp = *argv; if (*cp != '-') break; switch (cp[1]) { case 'd': opt_debug = 1; break; case 'P': port = atoi(cp + 2); break; case 's': opt_svr = 1; break; } } if (opt_svr) server(); else client(); return 0; } 

客户从不关闭sock 。 因此服务器的循环

  while ((read(acc_sock, buf, 1024)) != 0) { printf("%s", buf); memset(buf, 0, sizeof(buf)); } 

永不终止。 你需要一些机制来通知服务器所有命令的输出已经发送。 也许类似于HTTP分块传输编码。