任何人都可以帮我试图实现以下服务器和客户端吗?:
服务器:
#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()
存在问题,但我可能错了。有没有解决这个问题的方法?
答案 0 :(得分:1)
客户永远不会关闭sock
。因此服务器的循环
while ((read(acc_sock, buf, 1024)) != 0) {
printf("%s", buf);
memset(buf, 0, sizeof(buf));
}
永远不会终止。您需要一些机制来通知服务器已发送所有命令的输出。也许类似于HTTP分块传输编码。
答案 1 :(得分:1)
警告:这可能[或可能不]满足您的需求,因为我在下面的更正代码中颠倒了客户端和服务器循环的意义。正如我在上面的评论中提到的那样:
这样的应用程序的正常方向是客户端连接到服务器,客户端将命令[从stdin
读取]提供给服务器[执行popen
]和反馈结果。这就是ssh
的工作原理。您的方向相反。你得到的是你sshd
并等待ssh
连接,然后sshd
向ssh
发送命令。换句话说,应该切换各边的环。
扭转这一点是事情对我有意义的唯一方式。如果逆转对您所需的用例[井]不起作用,下面的代码可能仍会给您一些想法。
我通过引入标志字符的概念来解决挂起问题,以表示输出结束。我从PPP
上的RS-232
[点对点]协议借用了这个概念。
标志字符只是一个给定值(例如0x10
),它不太可能是正常数据的一部分。由于您的数据很可能是ascii
或utf-8
,因此可能会使用0x00-0x1F
范围内的任何[未使用]字符(即不使用tab,cr,换行符等)。< / p>
如果您需要来传输标志字符(即您的数据必须是完整的二进制范围0x00-0xFF
),我已经包含了一些实现转义的数据包编码/解码例程上面PPP
中使用的代码。我已对它们进行了编码,但实际上并没有将它们挂钩。在这种情况下,标志[和转义]字符可以是任何二进制值[通常为0xFF
和0xFE
分别]。
为简单起见,我将双方合并为一个.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;
}