如何将 system() 调用产生的所有 STDOUT 读取到用 C 编写的套接字中?
How to read all of STDOUT produced by system() call into a socket written in C?
以下是服务器套接字的代码片段,它读取客户端发送的 linux 命令,执行它并将输出发送回客户端:
while(1){
char command[200];
message_read = read(sock, command, sizeof(command));
if(message_read > 0){
command[message_read] = '[=10=]';
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] = '[=11=]';
//print the output somewhere
}
虽然“ls -al”、“pwd”或“whoami”等命令一次性给出输出,但客户端无法读取“ping”、“[=”等命令产生的全部输出27=]" 或 "du"。然而,当我多次调用上面的代码片段时,它会得到上面命令产生的其余输出(以块的形式。)
我尝试修改客户端函数如下:
send(sock, command, strlen(command), MSG_NOSIGNAL);
do{
if((message_read = read(sock, output, sizeof(output))) > 0){
output[message_read] = '[=12=]';
//print the output somewhere
}
}while(message_read);
以上解决方法挂掉了客户端程序。但是,在我终止服务器后,输出确实出现在客户端的 window 中!
另外,这次的输出都是零散的,缩进也很差。
Q1。发生什么事了?
Q2。如何解决?
您的代码发送和读取字符串的方式不够完善。
TCP是字节流。发送和读取之间没有一对一的关系。因此,发件人必须:
- 在发送字符串数据之前先发送字符串长度。
- 在字符串数据后发送一个唯一的终止符。
接收方必须:
- 读取长度后读取指定的数据量
- 阅读直到到达终止符。
此外,send()
/write()
和 recv()
/read()
可以比请求的字节少 return,因此需要在循环中调用它们(或者,在 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] = '[=10=]';
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 == '[=10=]') 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] = '[=10=]';
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);
}
}
或者,如果您提前不知道命令响应的长度,and/or不想将其全部缓冲在单个内存缓冲区中,那么您可以分块读取,边走边发送每个块,例如:
// 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);
}
以下是服务器套接字的代码片段,它读取客户端发送的 linux 命令,执行它并将输出发送回客户端:
while(1){
char command[200];
message_read = read(sock, command, sizeof(command));
if(message_read > 0){
command[message_read] = '[=10=]';
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] = '[=11=]';
//print the output somewhere
}
虽然“ls -al”、“pwd”或“whoami”等命令一次性给出输出,但客户端无法读取“ping”、“[=”等命令产生的全部输出27=]" 或 "du"。然而,当我多次调用上面的代码片段时,它会得到上面命令产生的其余输出(以块的形式。)
我尝试修改客户端函数如下:
send(sock, command, strlen(command), MSG_NOSIGNAL);
do{
if((message_read = read(sock, output, sizeof(output))) > 0){
output[message_read] = '[=12=]';
//print the output somewhere
}
}while(message_read);
以上解决方法挂掉了客户端程序。但是,在我终止服务器后,输出确实出现在客户端的 window 中! 另外,这次的输出都是零散的,缩进也很差。
Q1。发生什么事了?
Q2。如何解决?
您的代码发送和读取字符串的方式不够完善。
TCP是字节流。发送和读取之间没有一对一的关系。因此,发件人必须:
- 在发送字符串数据之前先发送字符串长度。
- 在字符串数据后发送一个唯一的终止符。
接收方必须:
- 读取长度后读取指定的数据量
- 阅读直到到达终止符。
此外,send()
/write()
和 recv()
/read()
可以比请求的字节少 return,因此需要在循环中调用它们(或者,在 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] = '[=10=]';
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 == '[=10=]') 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] = '[=10=]';
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);
}
}
或者,如果您提前不知道命令响应的长度,and/or不想将其全部缓冲在单个内存缓冲区中,那么您可以分块读取,边走边发送每个块,例如:
// 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);
}