引言:为什么代码写了printf(“Hello World!\r\n”)后,屏幕上就能看到打印输出呢?如何将ptintf的打印重定向到我们想要的流控里呢?如何实现printf的打印,输出到客户端的回显中重新显示呢?
当进程起来后,会打开3个描述符:标准输入,标准输出,和标准错误,printf会将打印的信息,输入给标准输出的fd,该fd会将信息刷新给屏幕显示;所以如果想实现printf的重定向,只需要将标准输出的fd,重新定向到我们的流控,比如socket的fd,当有打印信息时,实际会给socket刷新数据;如下图所示
如何实现stdout重定向到newfd呢,关键函数需要用到dup2
以下是服务端和客户端代码,服务端将fd重新指向到建立连接的socket,使得printf直接输出到客户端上
Server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
int server_fd, new_socket;
long valread;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *message = "Hello from server";
// 创建一个socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置socket地址和端口
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8888);
// 绑定socket
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(server_fd, 10) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
// 等待连接创建
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0)
{
perror("accept");
exit(EXIT_FAILURE);
}
// // 读取客户端发送的数据
// valread = read(new_socket, buffer, 1024);
// printf("Client message: %s\n", buffer);
// // 向客户端发送数据
// send(new_socket, message, strlen(message), 0);
int refd_out = dup2(new_socket, STDOUT_FILENO);
if (refd_out == -1)
{
return -1;
}
/* !!!关键,设置stdout为非换行符输出 */
setvbuf(stdout, NULL, _IOLBF, 0);
int i = 0;
while (1)
{
i++;
if (i > 20)
{
i = 0;
}
printf("@@@[%d]\r\n", i);
write(new_socket, "ret", strlen("ret"));
send(STDOUT_FILENO, "ret1", strlen("ret1"), 0);
sleep(1);
}
// 关闭连接
close(new_socket);
close(server_fd);
close(refd_out);
return 0;
}
Client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(void) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("connect");
exit(EXIT_FAILURE);
}
// Send data to server
// ...
// Wait for server response
// ...
char buf[256] = {0};
int ret = 0;
while(1)
{
ret = read(sock, buf, sizeof(buf));
if(ret){printf("Server: %s\n", buf); memset(buf, 0, sizeof(buf));}
}
close(sock);
return 0;
}
可以看到在重定向后,往标准输出的打印,已经重定向到了socket中,
以下三句话的输出方向都是一样的
printf("@@@[%d]\r\n", i);
write(new_socket, "ret", strlen("ret"));
send(STDOUT_FILENO, "ret1", strlen("ret1"), 0);
特别值得注意的是,printf重定向后,需要设置printf的输出缓存为 不缓存,不然只有遇到换行才会输出
原文链接
扫一扫关注见更多