Linux下TCP输出打印消息

一、需求

有时我们的程序在产品上跑,不知道那天会出现问题,再重启程序才能看到打印的消息,但是重启后问题又不出现了,所以就需要在出问题的时候能看到程序的打印消息。

二、程序设计

用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)

 






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 追踪Linux TCP/IP代码运行的过程,可以通过以下几个步骤实施: 首先,需要获得Linux内核的源代码,并找到TCP/IP协议栈的相关代码文件。这些文件通常位于“net”目录下,涉及到TCP/IP的部分大多在“net/ipv4”或“net/ipv6”目录下。 然后,我们可以选择使用调试器(如GDB)或者打印日志的方式来跟踪代码运行。如果选择使用调试器,可以在代码中设置断点,观察程序在断点处的执行情况,查看变量的值和函数的调用关系。如果使用打印日志的方式,可以在关键的代码段插入打印语句,输出相关变量的值和执行路径。 接着,我们需要了解TCP/IP协议栈的基本工作原理。具体来说,可以关注TCP的连接建立、数据传输和连接关闭等过程,以及IP协议的路由选择和分组转发等操作。 在追踪过程中,可以选择从应用层开始,如HTTP请求的发送和响应的接收。通过查看应用层的调用栈以及涉及的网络函数,可以逐步跟进到TCP/IP协议栈的代码中。同时,还可以通过观察网络数据包的发送和接收过程来了解底层数据在协议栈中的处理过程。 在跟踪代码的过程中,需要仔细观察代码中的注释和文档,以及相关的技术文档和论文,以便理解代码的设计思路和实现细节。 最后,可以通过调试器或分析日志的结果来验证自己对代码运行过程的理解,并进行必要的修复和改进。 总之,追踪Linux TCP/IP代码的运行是一个复杂而具有挑战性的任务,需要对Linux内核和网络协议有一定的了解和熟悉。同时,需要具备一定的编程和调试能力,以便在代码中找到关键的位置,并通过调试工具或日志信息来进行代码分析。 ### 回答2: 要追踪Linux TCP/IP代码运行,可以按照以下步骤进行: 1. 首先,了解TCP/IP协议栈的基本原理和相关概念,包括网络层、传输层和应用层的功能和协议。 2. 掌握Linux内核开发的基础知识,包括Linux内核的编译、调试和运行环境的搭建。 3. 下载并编译Linux内核源代码,可以从官方网站或开源代码库获取最新的Linux内核源码。 4. 在源代码中找到相关的TCP/IP代码,可以通过在源代码中搜索关键字或使用开发工具进行定位。 5. 使用调试工具来分析和追踪代码的运行。Linux内核提供了一些调试工具如Ftrace、Kprobes和SystemTap等,可以用于监视和分析内核函数的调用和运行过程。 6. 添加自定义的调试输出或断点来获取更详细的代码执行信息。通过在代码中插入printk语句或断点来观察特定变量的值或代码执行路径。 7. 使用网络抓包工具来捕获和分析网络数据包,可以验证TCP/IP代码的正确性和性能。 8. 进行测试和验证,使用各种测试工具和场景来验证TCP/IP代码的正确性和性能。 9. 探索并阅读现有的文档、论文和开发者社区的讨论,可以从中获取更多关于Linux TCP/IP代码的运行和调试技巧。 总之,追踪Linux TCP/IP代码运行需要深入理解 TCP/IP协议栈的原理,掌握Linux内核开发的基础知识,使用调试工具和网络抓包工具来分析和验证代码的运行。同时,与开发者社区保持联系,探索和学习他人的经验和技巧。 ### 回答3: 要追踪Linux TCP/IP代码的运行,首先需要一个源代码的PDF文件。我们可以从官方Linux内核网站上下载相关的源码文件,然后将其导出为PDF格式。 追踪代码运行的第一步是了解整体的调用流程。打开PDF文件,可以从文件的目录中找到TCP/IP模块的相关代码。这些文件包括传输层代码(如tcp.c)和网络层代码(如ip.c),它们负责TCP/IP协议的实现。 在PDF文件中,我们可以使用搜索功能找到特定的函数名或关键字。比如,如果希望追踪TCP的建立过程,可以搜索"tcp_accept"函数。找到这个函数后,可以查看它的调用关系,看它在哪些地方被调用,以及它又调用了哪些函数。 在追踪过程中,要注意一些关键的数据结构,例如TCP控制块(TCB)和IP包头。这些结构中包含了关键的信息,如IP地址、端口号和序列号等。可以通过搜索这些结构的定义,并在代码中追踪它们的使用。 另外,还可以使用调试工具来加速追踪的过程。在Linux系统中,可以使用GDB(GNU调试器)来逐步执行代码,并观察变量的值。这可以帮助我们更直观地理解代码的执行流程。 追踪Linux TCP/IP代码的运行需要耐心和坚持,需要仔细阅读和理解大量的代码。通过结合源代码的PDF文件和调试工具的使用,我们可以更好地理解代码的逻辑和执行过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值