有人可以帮我实现以下服务器和客户端吗?
服务器:
#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。
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。
popen()
NULL
问题是代码在第一个命令之后突然停止工作。命令的传输和命令的输出是令人满意的,但是在打印第一个命令的输出后,程序只是停止工作。我认为是有问题fgets(),但是我可能错了。这个问题有解决方案吗?
fgets()
警告: 这可能(或可能不)满足您的需求,因为我在下面的更正代码中反转了客户端和服务器循环的含义。正如我在上面的评论中提到的:
像这样的应用程序的正常方向是,客户端连接到服务器,并且客户端将命令[从stdin] 读取到服务器[这样做popen]并反馈结果。就是这样ssh。您的方向相反。您所拥有的是您开火sshd并等待ssh连接,然后sshd将命令发送至ssh。换句话说,应切换各侧的回路。
popen
ssh
sshd
扭转这种局面是使事情对我有意义的唯一途径。如果在您想要的用例中逆转无法正常进行,则下面的代码可能仍会为您提供一些建议。
我通过引入 标记 字符的概念来表示输出结束来解决挂起问题。我从PPP[点对点]协议中借用了这个概念RS-232。
PPP
RS-232
标志字符只是一个给定的值(例如0x10),不太可能是正常数据的一部分。由于您的数据很有可能是ascii或utf-8,因此0x00-0x1F可以使用该范围内的任何[未使用]字符(即,不要使用tab,cr,换行符等)。
0x10
ascii
utf-8
0x00-0x1F
如果您 需要 发送标志字符(即您的数据必须是完整的二进制范围0x00-0xFF),我已经包括了一些实现PPP上面使用的转义码的数据包编码/解码例程。我编写它们,但实际上并没有钩他们。在这种情况下,该标志[和逃生]字符可以是 任意 的二进制值通常0xFF和0xFE分别。
0x00-0xFF
0xFF
0xFE
为简单起见,我将双方合并为一个.c文件。用-s[first] 调用服务器。
.c
-s
无论如何,这是经过测试的代码[请原谅免费的样式清理]:
// 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; }