一、需求
有时我们的程序在产品上跑,不知道那天会出现问题,再重启程序才能看到打印的消息,但是重启后问题又不出现了,所以就需要在出问题的时候能看到程序的打印消息。
二、程序设计
用TCP来发送打印消息,服务端只需要调用int CreaSendPthread(),而客户端只需要调用int CreaRecvPthread(char *ipaddr)就可以了,端口已经默认绑定一样了。在工程里面在需要打印的地方先#inlcude 头,用ro_printf进行打印就可以了。
/*debugcom.h*/
#ifndef _DEBUGCOM_H_
#define _DEBUGCOM_H_
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAXNUM 10
#define ro_printf(format, ...) do \
{ \
sprintf(Cache, ""format"", ##__VA_ARGS__);\
if (cpsize <= PACKSIZE-PERMSGSIZE)\
{\
strncpy(sendbuf+cpsize, Cache, PERMSGSIZE);\
cpsize += PERMSGSIZE;\
}else\
{\
cpsize = 0;\
}\
}while(0)
#define PACKSIZE (40*50)
#define PERMSGSIZE 40
#define PORT 8186
int cpsize;
char recvbuf[PACKSIZE];
char sendbuf[PACKSIZE];
char Cache[PERMSGSIZE];
int create_socket();
int sock_bind(int lisfd, int port);
int sock_listen(int lisfd, int max_con);
int sock_accept(int lisfd);
int sock_connect(int clifd, char *ipaddr, int port);
int sock_send(int sockfd, void *buf, size_t len, int flags);
int sock_recv(int sockfd, void *buf, size_t len, int flags);
void *DebugSendMain(void *arg);
void *DebugRecvMain(void *arg);
int CreaSendPthread();
int CreaRecvPthread(char *ipaddr);
#endif
/*debugcom.c*/
#include "debugcom.h"
int create_socket()
{
int lisfd;
lisfd = socket(AF_INET, SOCK_STREAM, 0);//IPV4 数据流 TCP类型
if (lisfd == -1)
{
perror("create_socket failed!\n");
exit(1);
}
return lisfd;
}
int sock_bind(int lisfd, int port)
{
struct sockaddr_in myaddr;
memset((char *)&myaddr, 0, sizeof(struct sockaddr_in));//清零
myaddr.sin_family = AF_INET;//IPV4
myaddr.sin_port = htons(port);//端口
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);//允许连接到所有本地地址上
if (bind(lisfd, (struct sockaddr *)&myaddr, sizeof(struct sockaddr))==-1)
{
perror("sock_bind failed!\n");
exit(1);
}
return 0;
}
int sock_listen(int lisfd, int max_con)
{
if (listen(lisfd, max_con)==-1)
{
perror("sock_listen failed!\n");
exit(1);
}
return 0;
}
int sock_accept(int lisfd)
{
struct sockaddr_in remoaddr;
int clifd;
socklen_t size = sizeof(struct sockaddr);
memset((char *)&remoaddr, 0, sizeof(struct sockaddr));
clifd = accept(lisfd, (struct sockaddr *)&remoaddr, &size);
if (clifd == -1)
{
perror("sock_accept failed!\n");
exit(1);
}
printf("clifd: %d\n", clifd);
printf("connected from %s, port: %d\n", inet_ntoa(remoaddr.sin_addr), ntohs(remoaddr.sin_port));
return clifd;
}
int sock_connect(int clifd, char *ipaddr, int port)
{
struct sockaddr_in remoaddr;
memset((char *)&remoaddr, 0, sizeof(struct sockaddr_in));
remoaddr.sin_family = AF_INET;//IPV4地址
remoaddr.sin_port = htons(port);//端口
remoaddr.sin_addr.s_addr = inet_addr(ipaddr);//服务器端IP地址
if (connect(clifd, (struct sockaddr *)&remoaddr, sizeof(struct sockaddr))==-1)
{
perror("sock_connect failed!\n");
exit(1);
}
return 0;
}
int sock_send(int sockfd, void *buf, size_t len, int flags)
{
int nbytes;
nbytes = send(sockfd, buf, len, flags);
if (nbytes == -1)
{
perror("sock_send failed!\n");
}
return nbytes;
}
int sock_recv(int sockfd, void *buf, size_t len, int flags)
{
int nbytes;
nbytes = recv(sockfd, buf, len, flags);
if (nbytes == -1)
{
perror("sock_recv failed!\n");
}
return nbytes;
}
void *DebugSendMain(void *arg)
{
int lisfd, clifd, sockfd;
int i, n;
int nready;
int client[FD_SETSIZE];
int maxfd, maxi;
fd_set rset;
fd_set allset;
signal(SIGPIPE, SIG_IGN);//忽略对端TCP终止的信号
cpsize = 0;
bzero(recvbuf, sizeof(recvbuf));
bzero(sendbuf, sizeof(sendbuf));
lisfd = create_socket();
int opt = 1;
setsockopt(lisfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//地址重用
sock_bind(lisfd, PORT);
sock_listen(lisfd, MAXNUM);
maxfd = lisfd;
maxi = -1;
for (i=0; i<FD_SETSIZE; i++)//初始化客户端数组,为-1表示可用
{
client[i] = -1;
}
FD_ZERO(&allset);
FD_SET(lisfd, &allset);
while(1)
{
rset = allset;
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if (FD_ISSET(lisfd, &rset))//新的客户端连接
{
clifd = sock_accept(lisfd);
for (i=0; i<FD_SETSIZE; i++)
{
if (client[i] < 0)
{
client[i] = clifd;//保存文件描述符
break;
}
}
if (i == FD_SETSIZE)
{
printf("too many client!\n");
exit(1);
}
FD_SET(clifd, &allset);//增加一个描述符到集合里
if (clifd > maxfd)
{
maxfd = clifd;
}
if (i > maxi)
{
maxi = i;
}
if (--nready <= 0)
{
continue;//没有可读的数据
}
}
for (i=0; i<= maxi; i++)//检测所有客户端的数据
{
if ((sockfd = client[i])<0)
{
continue;
}
if (FD_ISSET(sockfd, &rset))
{
n = sock_recv(sockfd, recvbuf, sizeof(recvbuf), 0);
if (n == 0)
{
printf("The other side has been closed.\n");
fflush(stdout);
close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
}
else
{
//printf("recv: %s\n", recvbuf);
sock_send(sockfd, sendbuf, sizeof(sendbuf), 0);
//cpsize = 0;
}
if (--nready <= 0)
{
break;
}
}
}
}
}
void *DebugRecvMain(void *arg)
{
int i;
int clifd;
time_t t;
char *ptime = (char*)malloc(25);
char *p = (char*)malloc(8);
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
bzero(recvbuf, sizeof(recvbuf));
bzero(sendbuf, sizeof(sendbuf));
clifd = create_socket();
sock_connect(clifd, (char*)arg, PORT);
strcpy(sendbuf, "GetDebugMessage");
while(1)
{
//sleep(4);
sock_send(clifd, sendbuf, sizeof(sendbuf), 0);
sock_recv(clifd, recvbuf, sizeof(recvbuf), 0);
for (i=0; i<PACKSIZE; i += PERMSGSIZE)
{
strncpy(Cache, recvbuf+i, PERMSGSIZE);
t = time(NULL);
ptime = ctime(&t);
strncpy(p, ptime+11, 8);
printf("Debug %s: %s\n", p, Cache);
usleep(150*1000);
}
}
free(p);
free(ptime);
close(clifd);
}
int CreaSendPthread()
{
pthread_t Sendid;
if (pthread_create(&Sendid, NULL, (void *)DebugSendMain, NULL) == -1)
{
printf("pthread_create DebugMain failed.\n");
exit(1);
}
return 0;
}
int CreaRecvPthread(char *ipaddr)
{
pthread_t Recvid;
if (pthread_create(&Recvid, NULL, (void *)DebugRecvMain, ipaddr) == -1)
{
printf("pthread_create DebugMain failed.\n");
exit(1);
}
return 0;
}
三、使用示例
给出简单的服务端和客户端
/*server.c*/
#include "debugcom.h"
void *fun(void)
{
while(1)
{
ro_printf("testtest");
ro_printf("TESTTEST");
sleep(1);
}
}
int main(int argc, char **argv)
{
pthread_t pid;
if (pthread_create(&pid, NULL, (void *)fun, NULL) == -1)
{
printf("pthread_create failed.\n");
exit(1);
}
CreaSendPthread();
pause();
return 0;
}
/*client.c*/
#include "debugcom.h"
int main(int argc, char **argv)
{
char *ip = (char*)malloc(16);
printf("Please input the IPC address: ");
scanf("%s", ip);
CreaRecvPthread(ip);
pause();
free(ip);
return 0;
}
#makefile
CFLAGS = -Wall -g
CC = gcc
TARGET1 = server
TARGET2 = client
LIBS = -pthread
all:$(TARGET1) $(TARGET2)
$(TARGET1):$(TARGET1).o debugcom.o
$(CC) $(CFLAGS) $(LIBS) -o $@ $^
$(TARGET2):$(TARGET2).o debugcom.o
$(CC) $(CFLAGS) $(LIBS) -o $@ $^
%.o:%.c %.h
$(CC) -c $(CFLAGS) $(LIBS) -o $@ $<
clean:
rm -f $(TARGET1) $(TARGET2) *.o
以下是window下的客户端版本:
/*client.c*/
#include <iostream>
#include <cstdio>
#include <windows.h>
#include <cstring>
#include <Winsock2.h>
#include <time.h>
using namespace std;
#define PACKSIZE (40*50)
#define PERMSGSIZE 40
char recvbuf[PACKSIZE];
char sendbuf[PACKSIZE];
char Cache[PERMSGSIZE];
int main()
{
int err;
int i;
time_t t;
char ptime[64];
char ip[16];
printf("Please input the IPC address: ");
scanf("%s", ip);
if (strlen(ip) > 16)
{
printf("ip format false.\n");
exit(1);
}
printf("Init ...\n");
// 加载socket动态链接库(dll)
WORD wVersionRequested;
WSADATA wsaData; // 这结构是用于接收Wjndows Socket的结构信息的
wVersionRequested = MAKEWORD(1, 1); // 请求1.1版本的WinSock库
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return -1; // 返回值为零的时候是表示成功申请WSAStartup
}
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
// 检查这个低字节是不是1,高字节是不是1以确定是否我们所请求的1.1版本
// 否则的话,调用WSACleanup()清除信息,结束函数
WSACleanup( );
return -1;
}
// 创建socket操作,建立流式套接字,返回套接字号sockClient
// SOCKET socket(int af, int type, int protocol);
// 第一个参数,指定地址簇(TCP/IP只能是AF_INET,也可写成PF_INET)
// 第二个,选择套接字的类型(流式套接字),第三个,特定地址家族相关协议(0为自动)
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
// 将套接字sockClient与远程主机相连
// int connect( SOCKET s, const struct sockaddr* name, int namelen);
// 第一个参数:需要进行连接操作的套接字
// 第二个参数:设定所需要连接的地址信息
// 第三个参数:地址的长度
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr(ip); // 本地回路地址是127.0.0.1;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(8186);
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
memset(recvbuf, 0, PACKSIZE);
memset(sendbuf, 0, PACKSIZE);
memset(Cache, 0, PERMSGSIZE);
memset(ptime, 0, 25);
strcpy(sendbuf, "Hello");
while(1)
{
//Sleep(4*1000);
send(sockClient, sendbuf, sizeof(sendbuf), 0);
recv(sockClient, recvbuf, sizeof(recvbuf), 0);
for (i=0; i<PACKSIZE; i += PERMSGSIZE)
{
strncpy(Cache, recvbuf+i, PERMSGSIZE);
t = time(NULL);
strftime(ptime, sizeof(ptime), "%Y/%m/%d %X",localtime(&t));
printf("%s: %s\n", ptime, Cache);
Sleep(150);
}
}
closesocket(sockClient);
WSACleanup(); // 终止对套接字库的使用
system("pause");
return 0;
}
Note: window编译记得加载库(-lwsock32)