简单UDP日志程序

使用简单的宏Trace(...)就可以将字符串发送到指定的Udp服务端进行实时显示,Trace用法类似如printf传入可变参数,
它前两个参数是隐藏起来的分别是:文件名和行号,这样方便我们查找打印日志所在位置。

下面是完整客户端代码:

#include <stdio.h>
#include <stdarg.h>
#include <string>
#include <Windows.h>
#include <process.h>
#include <time.h>

#pragma comment(lib, "ws2_32.lib")

#define Trace CTrace(__FILE__, __LINE__)

class CUdpClient
{
public:
    CUdpClient(const std::string &ip=std::string("127.0.0.1"), unsigned short port=5050)
    {
        init(ip, port);
    }

    void init(const std::string &ip, unsigned short port)
    {
        sock_ = INVALID_SOCKET;
        memset(&sin_, 0, sizeof(sin_));
        sin_.sin_family = AF_INET;
        sin_.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
        sin_.sin_port = htons(port);
        addrLen_ = sizeof(sin_);
    }

    ~CUdpClient()
    {
        closesocket(sock_);
    }

    int send(const std::string &str)
    {
        if (INVALID_SOCKET == sock_)
        {
            sock_ = socket(AF_INET, SOCK_DGRAM, 0);
        }
        
        if (INVALID_SOCKET != sock_)
        {
            return sendto(sock_, str.c_str(), str.size(), 0, (SOCKADDR*)&sin_, addrLen_);
        }

        return 0;
    }


private:
    SOCKET sock_;
    sockaddr_in sin_;
    int addrLen_;
};

class CTrace
{
public:
    CTrace(const char *path, int line)
        : path_(path), line_(line)
    {
    }

    void __cdecl operator()(const char *fmt, ...) const
    {
        va_list ptr;
        va_start(ptr, fmt);
        char buffer[4096] = {0};
        std::string filename(path_);
        int ipos = filename.find_last_of('\\');
        filename = filename.substr(ipos+1, filename.size()-ipos+1);
        int iret = sprintf_s(buffer, sizeof(buffer), "[%s, %d] ", filename.c_str(), line_);
        if (-1 != iret)
        {
            vsnprintf_s(buffer+iret, sizeof(buffer)-iret-1, _TRUNCATE, fmt, ptr);
            udp_.send(buffer);
        }
        va_end(ptr);
    }

private:
    const char *path_;
    const int line_;
    static CUdpClient udp_;
};

CUdpClient CTrace::udp_;

///测试代码/
// 产生随机字符串
std::string BuildRandString(int num)
{
    static unsigned int s_add = 0;
    std::string ret;
    srand((unsigned int)time(NULL) + (s_add++));
    for (int i=0; i<num; )
    {
        char buf[17] = {0};
        _itoa_s(rand(), buf, 0x10);
        ret += buf;
        i += strlen(buf);
    }
    return ret.substr(0, num);
}

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsa;
    int iret = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (0 != iret)
    {
        return 0;
    }

    int num = 0;
    while (1)
    {
        char c = getchar(); // 每按下一个按键产生一条日志
        std::string str = BuildRandString(100);
        Trace("%d, %s", ++num, str.c_str()); // 用法
    }

    WSACleanup();
    system("pause");
    return 0;
}
服务端使用WTL做了个简单的单文档list box形式的界面用来展现
WTL库安装下载地址:http://www.cnblogs.com/tujiaw/archive/2011/02/26/3059245.html
首先在对话框创建的时候初始化socket,创建接收数据线程

WSADATA wsa;
        WSAStartup(MAKEWORD(2, 2), &wsa);
        InitSocket();
        m_threadHandle = (HANDLE)_beginthreadex(NULL, 0, RecvData, this, 0, NULL);
     CloseHandle(m_threadHandle);
void InitSocket()
    {
        sockaddr_in sin;
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
        sin.sin_port = htons(5050);
        m_sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (INVALID_SOCKET != m_sock)
        {
            if (SOCKET_ERROR == bind(m_sock, (SOCKADDR*)&sin, sizeof(sin)))
            {
                closesocket(m_sock);
            }
        }
    }
线程不断的接收数据并显示到界面上
unsigned int __stdcall RecvData(void* arg)
{
    CMainFrame *p = (CMainFrame*)arg;
    if (NULL == p)
    {
        return 0;
    }

    sockaddr_in clientAddr;
    int clientLen = sizeof(clientAddr);

    while(g_isRunning)
    {
        if (INVALID_SOCKET == p->m_sock)
        {
            break;
        }

        char buffer[4096] = {0};
        if (recvfrom(p->m_sock, buffer, sizeof(buffer), 0, (sockaddr*)&clientAddr, &clientLen) > 0)
        {
            p->m_view.AddString(buffer);
            p->m_view.PostMessageA(WM_VSCROLL, SB_BOTTOM);
        }
    }
        
    return 0;
}

双击单行可以将日志拷贝到剪切板上

LRESULT OnLButtonDClick(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        char buffer[4096] = {0};
        int curSel = GetCurSel();
        int len = GetText(curSel, buffer);
        
        BOOL bret = OpenClipboard();
        EmptyClipboard();
        HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, len + 1);
        char *p = (char*)GlobalLock(hGlobal);
        memset(p, 0, len+1);
        memcpy(p, buffer, strlen(buffer));
        bret = GlobalUnlock(hGlobal);
        HANDLE hResult = SetClipboardData(CF_TEXT, hGlobal);
        if (hResult)
        {
            memset(buffer, 0, sizeof(buffer));
            sprintf_s(buffer, "拷贝数据到剪切板成功,长度:%d", len);
            MessageBox(buffer);
        }
        CloseClipboard();
        return 0;
    }

源码是用vs2012编译的,只需要关注MainFrm.h文件就可以了,拷贝数据到剪切板代码在ShowTraceView.h中

下载地址:http://files.cnblogs.com/tujiaw/ShowTrace.rar

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值