前面我们实现了完整的TCP通信, 现在我们可以来考虑为通信加上日志记录的功能. 实现日志记录可以直接将数据写入文件中.
管道实现日志记录
这里采用管道实现进程间通信, 通过一个单独的进程来记录日志.
主要用到的就是文件的创建, 无名管道以及进程间通信的方法.
以下是删除了大部分的服务端代码, 只留下了修改过后的代码. 这里采用的是带缓冲的fwrite
函数实现向文件里写入数据. 因为采用的fwrite
是全缓冲, 所以在写完数据之后必须使用fflush
函数将缓冲区的内容真正的写入到文件中. 如果没有这个步骤, 那么日志就不会有任何数据记录了.
void doService(int servicefd)
{
...
// 初始化管道, 保证进程间通信
int fds[2];
pipe(fds);
pid_t pid;
if((pid = fork()) < 0)
exit(1);
else if(pid == 0){
FILE *fp;
char buf[1024];
int len;
while(1){
fp = fopen("log.txt", "a+");
len = read(fds[0], buf, sizeof(buf));
fwrite((void *)buf, sizeof(char), len, fp);
// 必须冲洗缓冲区
fflush(fp);
}
fclose(fp);
exit(1);
}
int n, eventNum;
while(1)
{
...
// 遍历多少个描述符状态改变
for(int i = 0; i < eventNum; i++)
{
// 将数据写入管道
write(fds[1], buf, n);
send(evts[i].data.fd, buf, n, 0);
}
}
}
当然也可以使用不带缓冲区的函数, 这在linux环境下可以运行. 同样, lseek
函数必须加上, 不然每次服务端都会将数据清除在重新记录.
void doService(int servicefd)
{
...
// 初始化管道, 保证进程间通信
int fds[2];
pipe(fds);
pid_t pid;
if((pid = fork()) < 0)
exit(1);
else if(pid == 0){
int filefd = open("log.txt", O_RDWR | O_CREAT, 0755);
lseek(filefd, SEEK_END, 0);
int len;
char buf[1024];
while(1){
// 从管道读出数据
len = read(fds[0], buf, sizeof(buf));
write(filefd, buf, len);
}
close(filefd);
exit(0);
}
int n, eventNum;
while(1)
{
...
// 遍历多少个描述符状态改变
for(int i = 0; i < eventNum; i++)
{
// 将数据写入管道
write(fds[1], buf, n);
send(evts[i].data.fd, buf, n, 0);
}
}
}
小结
其实记录日志对服务端也是很重要的, 本节只是介绍了一个很简单的方法, 并且日志也不一定是记录的, 这个以后有时间或者有兴趣的可以尝试更加好的方法和记录方式. 同样, 管道在网络编程中也有多种不同的使用方法. 如 : dup
绑定输出, **信号统一(在进程池可以看到)**等.