码迷,mamicode.com
首页 > 编程语言 > 详细

c++ Ping 通过线程处理Ping功能(MFC)

时间:2020-06-16 15:00:21      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:packet   mod   处理   ping   mic   HERE   text   常用   mtime   

Ping功能是测试网络是否连接的有效方式。通常我们需要通过ping来验证网络连接是否正常,这就需要我们经常用到ping功能。

ping是有一定的阻塞,如果频繁使用会导致应用程序出现阻塞现象,为了避免这种情况的发生,我们这里举例用线程的方式对网络进行ping来验证网络是否连接正常:

  1. UI布局技术图片
  2. 添加按键处理:技术图片OnBn1ClickedSend技术图片OnBn2ClickedClear技术图片OnBn3ClickedReturn
  3. 添加线程处理:
    技术图片
    DWORD myPingDlg::onMyThread1(LPVOID lpParameter)
    {
        // TODO: Add your control notification handler code here
        int i;
        BOOL bResult;
        CString str;
        CPing objPing;
        MyStruct1 *pPingMessage;
        i = 0;
        pPingMessage = (MyStruct1*)lpParameter;
        do
        {
            i++;
            bResult = objPing.Ping(pPingMessage->pDestIP, &pPingMessage->reply, pPingMessage->dwBytes);
            if (bResult == TRUE)
            {
                ::PostMessage(pPingMessage->hwnd, WM_PING, 0, (LPARAM)pPingMessage);
            }
            else
            {
                ::PostMessage(pPingMessage->hwnd, WM_PING, 1, (LPARAM)i);
            }
            Sleep(1000);
        } while (bEndless);
        ::PostMessage(pPingMessage->hwnd, WM_PING, 2, NULL);
        return 0;
    }
    onMyThread1

     

  4. 线程向对话框传递消息处理:
    技术图片
    LRESULT myPingDlg::onPingMessage(WPARAM wParam, LPARAM lParam)
    {
        UNREFERENCED_PARAMETER(wParam);
        UNREFERENCED_PARAMETER(lParam);
        WORD i;
        CString str,org;
        MyStruct1 *pMyPingInfo;
        UpdateData(true);
        org = m_edit1_ping;
        i = (WORD)wParam;
        if (i == 0)
        {
            pMyPingInfo = (MyStruct1*)lParam;
            str.Format(_T("Reply from : bytes=%d time=%ldms TTL=%ld\r\n"), pMyPingInfo->reply.m_dwBytes,pMyPingInfo->reply.m_dwRoundTripTime,pMyPingInfo->reply.m_dwTTL);
            str.Insert(11, pMyPingInfo->pDestIP);
        }
        else if (i == 1)
        {
            str.Format(_T("Failed: Ping did not response at %d package.\r\n"), lParam);
        }
        else if (i == 2)
        {
            str = _T("\r\nPing Test STOP !!!\r\n");
            CButton*pbutton = (CButton*)this->GetDlgItem(IDC_BUTTON1);
            SetDlgItemText(IDC_BUTTON1, _T("PingRetry"));
            SetDlgItemText(IDC_BUTTON3, _T("Return"));
            pbutton->EnableWindow(TRUE);
            CEdit*pEditCtl = (CEdit*)this->GetDlgItem(IDC_EDIT3);
            pEditCtl->EnableWindow(TRUE);
            pEditCtl = (CEdit*)this->GetDlgItem(IDC_EDIT4);
            pEditCtl->EnableWindow(TRUE);
        }
        SetDlgItemText(IDC_EDIT1, org+str);
        CEdit*pCtl = (CEdit*)this->GetDlgItem(IDC_EDIT1);
        pCtl->LineScroll(pCtl->GetLineCount());
        return 0;
    }
    onPingMessage

     

  5. 完整代码:
    技术图片
    // myPingDlg.cpp : implementation file
    //
    
    
    #include "stdafx.h"
    #include "MicrohardTest.h"
    #include "myPingDlg.h"
    #include "afxdialogex.h"
    //user
    #include "MicrohardTestDlg.h"
    #include "Source.h"
    #include <winsock2.h>
    #include <stdio.h>
    #include <string.h>
    
    // myPingDlg dialog
    static BOOL bEndless;
    IMPLEMENT_DYNAMIC(myPingDlg, CDialogEx)
    
    myPingDlg::myPingDlg(CWnd* pParent /*=NULL*/)
        : CDialogEx(myPingDlg::IDD, pParent)
        , m_edit1_ping(_T(""))
        , m_edit2_web(_T("192.168.168.1"))/*dw=0xc0a8a801; ping 192.168.168.1 -l 65000 –t*/
        , m_dw_edit4_bytes_of_package(32)
    {
    
    }
    
    myPingDlg::~myPingDlg()
    {
    }
    
    void myPingDlg::DoDataExchange(CDataExchange* pDX)
    {
        CDialogEx::DoDataExchange(pDX);
        DDX_Text(pDX, IDC_EDIT1, m_edit1_ping);
        DDX_Text(pDX, IDC_EDIT3, m_edit2_web);
        DDX_Text(pDX, IDC_EDIT4, m_dw_edit4_bytes_of_package);
        DDV_MinMaxUInt(pDX, m_dw_edit4_bytes_of_package, 1, 65000);
    }
    
    
    BEGIN_MESSAGE_MAP(myPingDlg, CDialogEx)
        ON_BN_CLICKED(IDC_BUTTON3, &myPingDlg::OnBn3ClickedReturn)
        ON_BN_CLICKED(IDC_BUTTON1, &myPingDlg::OnBn1ClickedSend)
        ON_BN_CLICKED(IDC_BUTTON2, &myPingDlg::OnBn2ClickedClear)
        ON_MESSAGE(WM_PING, onPingMessage)
    END_MESSAGE_MAP()
    
    
    // myPingDlg message handlers
    
    
    void myPingDlg::OnBn3ClickedReturn()
    {
        // TODO: Add your control notification handler code here
        if (bEndless == TRUE)
        {
            bEndless = FALSE;
        }
        else
        {
            myPingDlg::OnOK();
            CMicrohardTestDlg mdlg;
            mdlg.DoModal();
        }
    }
    
    
    void myPingDlg::OnBn1ClickedSend()
    {
        // TODO: Add your control notification handler code here
        int nCount = 0;
        char *szDestIP;
        char szIp0[100][100];
        CString str;
        CPing objPing;
        char szDomain[256] = { 0 };
    
        //Get ip address from domain
        UpdateData(true);
        if (m_dw_edit4_bytes_of_package < 1){
            SetDlgItemText(IDC_EDIT4, "1");
            return;
        }
        else if (m_dw_edit4_bytes_of_package>65000)
        {
            SetDlgItemText(IDC_EDIT4,"65000");
            return;
        }
        GetDlgItem(IDC_EDIT3)->GetWindowText(str);
        strcpy_s(szDomain, str);
        BOOL bResult = objPing.GetIpByDomainName(szDomain, szIp0, &nCount);
        if (bResult == TRUE)
        {
            szDestIP = &szIp0[0][0];
            DWORD dwIP = ntohl(inet_addr(szDestIP));
            CIPAddressCtrl*pIP = (CIPAddressCtrl*)this->GetDlgItem(IDC_IPADDRESS2);
            pIP->SetAddress(dwIP);
            if (nCount != 0)
            {
                CButton*pbutton = (CButton*)this->GetDlgItem(IDC_BUTTON1);
                pbutton->EnableWindow(FALSE);
                SetDlgItemText(IDC_BUTTON1, _T("PingRunning"));
                SetDlgItemText(IDC_BUTTON3, _T("PingStop"));
                str.Format(_T("Ping address: with %d bytes of a package:\r\n"), m_dw_edit4_bytes_of_package);
                str.Insert(13, szDestIP);
                m_edit1_ping = str;
                HANDLE hThread;
                MyStruct1 *pmystruct = new MyStruct1;
                pmystruct->hwnd = m_hWnd;
                pmystruct->pDestIP = szDestIP;
                pmystruct->dwBytes = m_dw_edit4_bytes_of_package;
                bEndless = TRUE;
                hThread = CreateThread(NULL, 0, onMyThread1, (LPVOID)pmystruct, 0, NULL);
                CloseHandle(hThread);
                CEdit*pEditCtl = (CEdit*)this->GetDlgItem(IDC_EDIT3);
                pEditCtl->EnableWindow(FALSE);
                pEditCtl = (CEdit*)this->GetDlgItem(IDC_EDIT4);
                pEditCtl->EnableWindow(FALSE);
            }
            else
            {
                m_edit1_ping = _T("Failed! Domain Can‘t Parser !!!");
            }
        }
        else
        {
            m_edit1_ping = _T("WSA Startup failed !!!"); 
        }
        UpdateData(FALSE);
    }
    
    
    void myPingDlg::OnBn2ClickedClear()
    {
        // TODO: Add your control notification handler code here
        UpdateData(TRUE);
        if (m_edit1_ping.IsEmpty()==false)
        {
            m_edit1_ping = _T("");
            UpdateData(FALSE);
        }
    }
    BOOL myPingDlg::PreTranslateMessage(MSG* pMsg)
    {
        if (pMsg->message == WM_KEYDOWN)
        {
            switch (pMsg->wParam)
            {
            case VK_ESCAPE:
            {
                              if (bEndless == TRUE)
                              {
                                  bEndless = FALSE;
                              }
                              return TRUE;
                              break;
            }
            case VK_RETURN:
            {
                              return TRUE;
                              break;
            }
            default:
                break;
            }
        }
        return CDialog::PreTranslateMessage(pMsg);
    }
    DWORD myPingDlg::onMyThread1(LPVOID lpParameter)
    {
        // TODO: Add your control notification handler code here
        int i;
        BOOL bResult;
        CString str;
        CPing objPing;
        MyStruct1 *pPingMessage;
        i = 0;
        pPingMessage = (MyStruct1*)lpParameter;
        do
        {
            i++;
            bResult = objPing.Ping(pPingMessage->pDestIP, &pPingMessage->reply, pPingMessage->dwBytes);
            if (bResult == TRUE)
            {
                ::PostMessage(pPingMessage->hwnd, WM_PING, 0, (LPARAM)pPingMessage);
            }
            else
            {
                ::PostMessage(pPingMessage->hwnd, WM_PING, 1, (LPARAM)i);
            }
            Sleep(1000);
        } while (bEndless);
        ::PostMessage(pPingMessage->hwnd, WM_PING, 2, NULL);
        return 0;
    }
    LRESULT myPingDlg::onPingMessage(WPARAM wParam, LPARAM lParam)
    {
        UNREFERENCED_PARAMETER(wParam);
        UNREFERENCED_PARAMETER(lParam);
        WORD i;
        CString str,org;
        MyStruct1 *pMyPingInfo;
        UpdateData(true);
        org = m_edit1_ping;
        i = (WORD)wParam;
        if (i == 0)
        {
            pMyPingInfo = (MyStruct1*)lParam;
            str.Format(_T("Reply from : bytes=%d time=%ldms TTL=%ld\r\n"), pMyPingInfo->reply.m_dwBytes,pMyPingInfo->reply.m_dwRoundTripTime,pMyPingInfo->reply.m_dwTTL);
            str.Insert(11, pMyPingInfo->pDestIP);
        }
        else if (i == 1)
        {
            str.Format(_T("Failed: Ping did not response at %d package.\r\n"), lParam);
        }
        else if (i == 2)
        {
            str = _T("\r\nPing Test STOP !!!\r\n");
            CButton*pbutton = (CButton*)this->GetDlgItem(IDC_BUTTON1);
            SetDlgItemText(IDC_BUTTON1, _T("PingRetry"));
            SetDlgItemText(IDC_BUTTON3, _T("Return"));
            pbutton->EnableWindow(TRUE);
            CEdit*pEditCtl = (CEdit*)this->GetDlgItem(IDC_EDIT3);
            pEditCtl->EnableWindow(TRUE);
            pEditCtl = (CEdit*)this->GetDlgItem(IDC_EDIT4);
            pEditCtl->EnableWindow(TRUE);
        }
        SetDlgItemText(IDC_EDIT1, org+str);
        CEdit*pCtl = (CEdit*)this->GetDlgItem(IDC_EDIT1);
        pCtl->LineScroll(pCtl->GetLineCount());
        return 0;
    }
    myPingDlg.cpp
    技术图片
    #pragma once
    
    
    #include "Source.h"
    
    // myPingDlg dialog
    #define WM_PING    WM_USER+2
    
    struct MyStruct1
    {
        HWND hwnd;
        char *pDestIP;
        PingReply reply;
        DWORD dwTimeOut;
        DWORD dwBytes;
    };
    class myPingDlg : public CDialogEx
    {
        DECLARE_DYNAMIC(myPingDlg)
    
    public:
        myPingDlg(CWnd* pParent = NULL);   // standard constructor
        virtual ~myPingDlg();
    
    // Dialog Data
        enum { IDD = IDD_DIALOG2 };
    private:
        static DWORD WINAPI onMyThread1(LPVOID lpParameter);
    
    protected:
        virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
        virtual BOOL PreTranslateMessage(MSG* pMsg);
        afx_msg LRESULT onPingMessage(WPARAM wParam, LPARAM lParam);
        DECLARE_MESSAGE_MAP()
    public:
        CString m_edit1_ping;
        afx_msg void OnBn3ClickedReturn();
        afx_msg void OnBn1ClickedSend();
        afx_msg void OnBn2ClickedClear();
        CString m_edit2_web;
        DWORD m_dw_edit4_bytes_of_package;
    };
    myPingDlg.h

     

  6. Ping处理代码:
    技术图片
    #include "stdafx.h"
    #include "Source.h"
    #include <iostream>
    
    USHORT CPing::s_usPacketSeq = 0;
    
    CPing::CPing() :m_szICMPData(NULL), m_bIsInitSucc(FALSE)
    {
        WSADATA WSAData;
        if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
        {
            /*如果初始化不成功则报错,GetLastError()返回发生的错误信息*/
            printf("WSAStartup() failed: %d\n", GetLastError());
            return;
        }
        m_event = WSACreateEvent();
        m_usCurrentProcID = (USHORT)GetCurrentProcessId();
        m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
        if (m_sockRaw == INVALID_SOCKET)
        {
            std::cerr << "WSASocket() failed:" << WSAGetLastError() << std::endl;  //10013 以一种访问权限不允许的方式做了一个访问套接字的尝试。
        }
        else
        {
            WSAEventSelect(m_sockRaw, m_event, FD_READ);
            m_bIsInitSucc = TRUE;
    
            m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));
    
            if (m_szICMPData == NULL)
            {
                m_bIsInitSucc = FALSE;
            }
        }
    }
    
    CPing::~CPing()
    {
        WSACleanup();
    
        if (NULL != m_szICMPData)
        {
            free(m_szICMPData);
            m_szICMPData = NULL;
        }
    }
    
    BOOL CPing::Ping(DWORD dwDestIP, PingReply *pPingReply,DWORD dwBytes, DWORD dwTimeout)
    {
        return PingCore(dwDestIP, pPingReply, dwTimeout, dwBytes);
    }
    
    BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply,DWORD dwBytes, DWORD dwTimeout)
    {
        if (NULL != szDestIP)
        {
    
            return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout, dwBytes);
        }
        return FALSE;
    }
    
    BOOL CPing::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout, DWORD dwBytes)
    {
        //判断初始化是否成功
        if (!m_bIsInitSucc)
        {
            return FALSE;
        }
    
        //配置SOCKET
        sockaddr_in sockaddrDest;
        sockaddrDest.sin_family = AF_INET;
        sockaddrDest.sin_addr.s_addr = dwDestIP;
        int nSockaddrDestSize = sizeof(sockaddrDest);
    
        //构建ICMP包
        //int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader);
        int nICMPDataSize = dwBytes + sizeof(ICMPHeader);
        ULONG ulSendTimestamp = GetTickCountCalibrate();
        USHORT usSeq = ++s_usPacketSeq;
        memset(m_szICMPData, 0, nICMPDataSize);
        ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData;
        pICMPHeader->m_byType = ECHO_REQUEST;
        pICMPHeader->m_byCode = 0;
        pICMPHeader->m_usID = m_usCurrentProcID;
        pICMPHeader->m_usSeq = usSeq;
        pICMPHeader->m_ulTimeStamp = ulSendTimestamp;
        pICMPHeader->m_usChecksum = CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);
    
        //发送ICMP报文
        if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR)
        {
            return FALSE;
        }
    
        //判断是否需要接收相应报文
        if (pPingReply == NULL)
        {
            return TRUE;
        }
    
        char recvbuf[65536] = { "\0" };
        while (TRUE)
        {
            //接收响应报文
            if (WSAWaitForMultipleEvents(1, &m_event, FALSE, 100, FALSE) != WSA_WAIT_TIMEOUT)
            {
                WSANETWORKEVENTS netEvent;
                WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent);
    
                if (netEvent.lNetworkEvents & FD_READ)
                {
                    ULONG nRecvTimestamp = GetTickCountCalibrate();
                    int nPacketSize = recvfrom(m_sockRaw, recvbuf, 65536, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);
                    if (nPacketSize != SOCKET_ERROR)
                    {
                        IPHeader *pIPHeader = (IPHeader*)recvbuf;
                        USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);
                        ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);
    
                        if (pICMPHeader->m_usID == m_usCurrentProcID //是当前进程发出的报文
                            && pICMPHeader->m_byType == ECHO_REPLY //是ICMP响应报文
                            && pICMPHeader->m_usSeq == usSeq //是本次请求报文的响应报文
                            )
                        {
                            pPingReply->m_usSeq = usSeq;
                            pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp;
                            pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);
                            pPingReply->m_dwTTL = pIPHeader->m_byTTL;
                            return TRUE;
                        }
                    }
                }
            }
            //超时
            if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout)
            {
                return FALSE;
            }
        }
    }
    
    USHORT CPing::CalCheckSum(USHORT *pBuffer, int nSize)
    {
        unsigned long ulCheckSum = 0;
        while (nSize > 1)
        {
            ulCheckSum += *pBuffer++;
            nSize -= sizeof(USHORT);
        }
        if (nSize)
        {
            ulCheckSum += *(UCHAR*)pBuffer;
        }
    
        ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff);
        ulCheckSum += (ulCheckSum >> 16);
    
        return (USHORT)(~ulCheckSum);
    }
    
    ULONG CPing::GetTickCountCalibrate()
    {
        static ULONG s_ulFirstCallTick = 0;
        static LONGLONG s_ullFirstCallTickMS = 0;
    
        SYSTEMTIME systemtime;
        FILETIME filetime;
        GetLocalTime(&systemtime);
        SystemTimeToFileTime(&systemtime, &filetime);
        LARGE_INTEGER liCurrentTime;
        liCurrentTime.HighPart = filetime.dwHighDateTime;
        liCurrentTime.LowPart = filetime.dwLowDateTime;
        LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;
    
        if (s_ulFirstCallTick == 0)
        {
            s_ulFirstCallTick = GetTickCount();
        }
        if (s_ullFirstCallTickMS == 0)
        {
            s_ullFirstCallTickMS = llCurrentTimeMS;
        }
    
        return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
    }
    
    BOOL CPing::GetIpByDomainName(char *szHost, char szIp[100][100], int *nCount)
    {
        WSADATA        wsaData;
        HOSTENT   *pHostEnt;
        int             nAdapter = 0;
        struct       sockaddr_in   sAddr;
        if (WSAStartup(0x0101, &wsaData))
        {
            return FALSE;
        }
    
        pHostEnt = gethostbyname(szHost);
        if (pHostEnt)
        {
            while (pHostEnt->h_addr_list[nAdapter])
            {
                memcpy(&sAddr.sin_addr.s_addr, pHostEnt->h_addr_list[nAdapter], pHostEnt->h_length);
                char  szBuffer[1024] = { 0 };
    
                sprintf_s(szBuffer, "%s", inet_ntoa(sAddr.sin_addr));
    
                strcpy_s(szIp[nAdapter], szBuffer);
                nAdapter++;
            }
    
            *nCount = nAdapter;
        }
        else
        {
            *nCount = 0;
        }
        WSACleanup();
        return TRUE;
    }
    Source.cpp
    技术图片
    #pragma once
    
    //在默认windows.h会包含winsock.h,当你包含winsock2.h就会冲突,因此在包含windows.h前需要定义一个宏,#define WIN32_LEAN_AND_MEAN ;去除winsock.h
    //要么将#include <winsock2.h>放在#include<windows.h>前面或者直接去掉#include<windows.h>
    
    #include <winsock2.h>
    #pragma comment(lib, "WS2_32")    // 链接到WS2_32.lib
    
    #define DEF_PACKET_SIZE 65000
    #define ECHO_REQUEST 8
    #define ECHO_REPLY 0
    
    struct IPHeader
    {
        BYTE m_byVerHLen; //4位版本+4位首部长度
        BYTE m_byTOS; //服务类型
        USHORT m_usTotalLen; //总长度
        USHORT m_usID; //标识
        USHORT m_usFlagFragOffset; //3位标志+13位片偏移
        BYTE m_byTTL; //TTL
        BYTE m_byProtocol; //协议
        USHORT m_usHChecksum; //首部检验和
        ULONG m_ulSrcIP; //源IP地址
        ULONG m_ulDestIP; //目的IP地址
    };
    
    struct ICMPHeader
    {
        BYTE m_byType; //类型
        BYTE m_byCode; //代码
        USHORT m_usChecksum; //检验和 
        USHORT m_usID; //标识符
        USHORT m_usSeq; //序号
        ULONG m_ulTimeStamp; //时间戳(非标准ICMP头部)
    };
    
    struct PingReply
    {
        USHORT m_usSeq;
        DWORD m_dwRoundTripTime;
        DWORD m_dwBytes;
        DWORD m_dwTTL;
    };
    
    class CPing
    {
    public:
        CPing();
        ~CPing();
        BOOL Ping(DWORD dwDestIP, PingReply *pPingReply = NULL, DWORD dwBytes = 32, DWORD dwTimeout = 2000);
        BOOL Ping(char *szDestIP, PingReply *pPingReply = NULL, DWORD dwBytes = 32, DWORD dwTimeout = 2000);
        BOOL  GetIpByDomainName(char *szHost, char szIp[100][100], int *nCount);
    private:
        BOOL PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout, DWORD dwBytes);
        USHORT CalCheckSum(USHORT *pBuffer, int nSize);
        ULONG GetTickCountCalibrate();
    private:
        SOCKET m_sockRaw;
        WSAEVENT m_event;
        USHORT m_usCurrentProcID;
        char *m_szICMPData;
        BOOL m_bIsInitSucc;
    private:
        static USHORT s_usPacketSeq;
    };
    Source.h

     

  7. 测试:技术图片

c++ Ping 通过线程处理Ping功能(MFC)

标签:packet   mod   处理   ping   mic   HERE   text   常用   mtime   

原文地址:https://www.cnblogs.com/lumao1122-Milolu/p/13140690.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!