如何将 system() 调用生成的所有 STDOUT 读取到用 C 编写的套接字中?



以下是来自服务器套接字的代码片段,它读取客户端发送的 linux 命令,执行它并将输出发送回客户端:

while(1){
char command[200];
message_read = read(sock, command, sizeof(command));
if(message_read > 0){
command[message_read] = '';
dup2(sock, STDOUT_FILENO);
dup2(sock, STDERR_FILENO);
system(command);
}
}

以下是向服务器发送命令并接收输出的客户端的代码片段:

char output[10240];
send(sock, command, strlen(command), MSG_NOSIGNAL);
if((message_read = read(sock, output, sizeof(output)))>0){
output[message_read] = '';
//print the output somewhere
}

虽然像"ls -al","pwd"或"whoami"这样的命令一次性给出输出,但客户端无法读取"ping","ps"或"du"等命令产生的全部输出。但是,当我多次调用上述代码片段时,它会让我获得上述命令生成的其余输出(以块为单位(。

我尝试按如下方式修改客户端函数:

send(sock, command, strlen(command), MSG_NOSIGNAL);
do{
if((message_read = read(sock, output, sizeof(output))) > 0){
output[message_read] = '';
//print the output somewhere
}
}while(message_read);

上述解决方案挂起了客户端程序。但是,在我杀死服务器后,输出确实显示在客户端的窗口中! 此外,这次输出全部分散且缩进不良。

问题 1.发生了什么事情?

问题 2.如何解决?

代码发送和读取字符串的方式是不够的。

TCP 是一个字节流。 发送和读取之间没有一对一的关系。 因此,发件人必须:

在发送字符串
  1. 数据之前发送字符串长度。
  2. 在字符串数据之后发送唯一的终止符。

并且接收方必须:

读取
  1. 长度,然后读取指定的数据量。
  2. 读取,直到到达终结符。

此外,send()/write()recv()/read()可以返回比请求更少的字节,因此需要在循环中调用它们(或者,在recv()的情况下,您可以使用MSG_WAITALL标志(。

尝试更多类似的东西:

// common functions ...
bool sendRaw(int sock, void *data, size_t len)
{
char *ptr = (char*) data;
while (len > 0) {
int sent = send(sock, ptr, len, MSG_NOSIGNAL);
if (sent < 0) return false;
ptr += sent;
len -= sent;
}
return true;
}
int recvRaw(int sock, void *data, size_t len)
{
char *ptr = (char*) data;
while (len > 0) {
int recvd = recv(sock, ptr, len, MSG_NOSIGNAL);
if (recvd <= 0) return recvd;
ptr += recvd;
len -= recvd;
}
return 1;
}
bool sendUInt32(int sock, uint32_t value)
{
value = htonl(value);
return sendRaw(sock, &value, sizeof(value));
}
uint32_t recvUInt32(int sock)
{
uint32_t value;
if (recvRaw(sock, &value, sizeof(value)) <= 0) return -1;
return ntohl(value);
}
bool sendString(int sock, const char *str)
{
uint32_t len = strlen(str);
if (!sendUInt32(sock, len)) return false;
return sendRaw(sock, str, len);
/* alternatively:
return sendRaw(sock, str, strlen(len) + 1);
*/
}
/*
bool grow(char **str, size_t *cap, size_t stepBy)
{
size_t newcap = cap + stepBy;
char *newstr = (char*) realloc(*str, newcap);
if (!newstr) return false;
*str = newstr;
*cap = newcap;
return true;
}
*/
char* recvString(int sock)
{
uint32_t len = recvUInt32(sock);
if (len == -1) return NULL;
char *str = (char*) malloc(len+1);
if (!str) return NULL;
if (recvRaw(sock, str, len) <= 0){
free(str);
return NULL;
}
str[len] = '';
return str;
/* alternatively:
char ch, *str = NULL;
size_t len = 0, cap = 0;
do{
if (recvRaw(sock, &ch, 1) <= 0){
free(str);
return NULL;
}
if (ch == '') break;
if (len == cap){
if (!grow(&str, &cap, 256)){
free(str);
return NULL;
}
}
str[len++] = ch;
}
while (1);
if (len == cap){
if (!grow(&str, &cap, 1)){
free(str);
return NULL;
}
}
str[len] = '';
return str;
*/
}
// server ...
char *command;
while ((command = recvString(sock)) != NULL){
// ...
system(command);
free(command);
// read from command's stdout until finished ...
if (!sendString(sock, output, outputLength)) break;
}
// client ...
if (sendString(sock, command)){
char *output = recvString(sock);
if (output){
//print the output somewhere
free(output);
}
}

或者,如果您事先不知道命令响应的长度,和/或不想将其全部缓冲在单个内存缓冲区中,那么您可以分块读取它,随时发送每个块,例如:

// common functions, see above ...
typedef struct _chunk
{
uint8_t size;
char data[256];
} chunk;
bool sendChunk(int sock, const chunk *chk)
{
uint8_t size = chk ? chk->size : 0;
if (!sendRaw(sock, &size, 1)) return false;
if (chk) return sendRaw(sock, chk->data, size);
return true;
}
bool recvChunk(int sock, chunk *chk)
{
if (recvRaw(sock, &(chk->size), 1) <= 0) return false;
if (chk->size) return recvRaw(sock, chk->data, chk->size);
return true;
}
// server ...
bool sendOutput(int sock)
{
chunk chk;
int size;
do{
// read from command's stdout ...
size = read(..., chk.data, sizeof(chk.data));
if (size <= 0) break;
chk.size = (uint8_t) size;
if (!sendChunk(sock, &chk)) return false;
}
while(1);

// tell client the data is finished ...
return sendChunk(sock, NULL);
}
char *command;
while ((command = recvString(sock)) != NULL){
// ...
system(command);
free(command);
if (!sendOutput(sock)) break;
}
// client ...
if (sendString(sock, command)){
chunk chk;
do{
if (!recvChunk(sock, &chk)) break;
if (chk.size == 0) break;
//print the chk.data somewhere
}
while (1);
}

最新更新