udp客户端与服务端相连时,握手情况的处理

        前段时间做打印机的打印程序,碰到了与对端连接问题,要实现的效果是不管是服务端先起来,还是打印机先起来,都要能握手成功,从而进行后面的信息传递,而打印出来。采用多线程+udp的方式,完美的解决了此问题。

处理逻辑:server给打印机一直发hello消息-收消息的状态;

                  而打印机一直处于收消息的状态,待收到hello消息后,给服务器发送一个“recvd"消息,退出循环;

                   待server 收到“recvd"消息后,已经建立连接,退出循环.

下面来看代码:

// udp_line_client.cpp : 定义控制台应用程序的入口点。
//


#include "stdafx.h"


#include "Node.h"  
int main()
{
    int ret;
    WSADATA wsa;
    //初始化套接字DLL  
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        cout << "套接字初始化失败!" << endl;
    }
    char MessageBuffer[1024];
    Node A;
    MSG ThreadMessage;
    A.StartMyThread();//启动新线程  
    //ret = A.SendStruct();//发送结构体数据  
    /*if (ret == 1)
    {
        system("pause");
        return 1;
    }*/
    while (1)//循环接收线程的消息  
    {
        if (PeekMessage(&ThreadMessage, NULL, 0, 0, PM_REMOVE))//搜集线程的消息  
        {
            if (ThreadMessage.message == WM_PARTY_MSG)//判断消息类型  
            {
                memcpy(MessageBuffer, (char *)ThreadMessage.lParam, 1024);//提取需要处理的网络信息  
                ret = A.DealMessage(MessageBuffer, 1024);//处理网络信息  
                if (ret == 1)
                {
                    cout << "connect success";
                    break;
                }
                    
            }
        }
    }


    system("pause");
    return TRUE;
}



#include "stdafx.h"
#include "Node.h"  
const int  MSG_STRUCT1 = 1;
const int MSG_STRUCT2 = 2;
const int MSG_STRUCT3 = 3;
#pragma comment(lib,"ws2_32.lib")
Node::Node()//构造函数  
{
    cout << "***************************************************" << endl;
    cout << "\n该程序主要包括:\n" << endl;
    cout << "1.多线程套接字程序设计框架" << endl;
    cout << "2.利用套接字发送结构体的方法" << endl;
    cout << "3.多个不同结构体的发送和接收" << endl;
    cout << "\n默认设置为:\n" << endl;
    cout << "1.各参与者IP地址为127.0.0.1(环回地址,用于单机测试)" << endl;
    cout << "2.编号为i的参与者端口号为10000+i" << endl;
    cout << "3.编号为1的参与者向编号为2的参与者发送测试数据" << endl;
    cout << "***************************************************" << endl;
    cout << "输入编号:" << endl;
    cin >> ID;
}


Node::~Node()//析构函数  
{


}


void Node::StartMyThread()//启动新线程  
{
    DWORD myMainThreadID = ::GetCurrentThreadId(); //获取当前线程也就是主线程的ID号  
    static ThreadParameter tp;//此处需设置为静态变量  
    tp.MainThreadID = myMainThreadID;
    tp.Port = 10000 + ID;//端口号初始化  
    HANDLE hThread = CreateThread(NULL, 0, StartAcceptThread, (LPVOID)&tp, 0, NULL);//创建新线程  
    CloseHandle(hThread);
}


DWORD WINAPI Node::StartAcceptThread(LPVOID lpData)//线程的启动函数,用来循环接收来自参与者发来的轮消息  
{
    ThreadParameter tp = *((ThreadParameter *)lpData);
    char RecveBuffer[20][4096];//为了保证,消息能够被安全处理,也就是在消息被线程处理之前,保证没有新的消息覆盖,我们定义了一个二维数组,相当于二十个缓冲区,用来接收数据  
    for (int i = 0; i<20; i++)
        memset(RecveBuffer[i], 0, 4096);
    SOCKET RecveSocket = ::socket(AF_INET, SOCK_DGRAM, 0);//创建UDP套接字  
    SOCKADDR_IN sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    sin.sin_port = ::htons(tp.Port);//端口号设置  
    if (::bind(RecveSocket, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)//地址与套接字绑定  
    {
        cout << "套接字绑定错误!" << endl;
        return 0;
    }






    /*以下是循环接收网络数据部分,在通信量大的时候,
    **可能数据不能及时传至主线程,而造成信息的覆盖
    **所以此处设置了一个二位数组RecveBuffer,相当于
    **20个长度为4096的缓冲区,可以适应通信量较大的情况。
    */
    SOCKADDR_IN addrRemote;
    int nLen = sizeof(addrRemote);
    int recvcount = 0;
    while (true)
    {
        SendStruct();




        if (recvcount == 20)
            recvcount = 0;
        //套接字接收函数  
        int nRet = ::recvfrom(RecveSocket, RecveBuffer[recvcount], 4096, 0, (sockaddr*)&addrRemote, &nLen);
        if (nRet == SOCKET_ERROR)
        {
            cout << "接受错误!" << endl;
            return 0;
        }
        if (nRet > 0)
        {
            //收到网络数据以后向主线程发送消息,并将数据交由主线程处理  
            if (PostThreadMessage(tp.MainThreadID, WM_PARTY_MSG, 0, (LPARAM)RecveBuffer[recvcount]) == 0)
            {
                cout << "向主线程发送消息失败" << GetLastError() << endl;
            }
            recvcount++;
        }
    }
    return 1;
}


//消息处理函数,该函数用处理不同的网络数据,对其进行分类响应  
int Node::DealMessage(char *MyMessage, int MessageLength)
{
    int StrcutFlag;
    //获取网络数据的标志位,根据不同的标志位进行不同的处理  
    memcpy(&StrcutFlag, MyMessage, sizeof(StrcutFlag));
    if (StrcutFlag == MSG_STRUCT1)//结构体1的数据  
    {
        m_struct1 ms1;
        memcpy(&ms1, MyMessage, sizeof(ms1));//取出结构体1  
        if (strstr(ms1.Value1,"recvd"))
        {
            return 1;
        }
        //cout << ms1.Value1 << endl;
    }    
    return 1;
}


//结构体发送函数  
int Node::SendStruct()
{
    char buf1[60], buf2[60], buf3[60];
    if (ID != 1)
        return 0;
    m_struct1 s1;
    //对不同的结构体进行不同的赋值  
    s1.flag = 1;
    strcpy_s(s1.Value1, sizeof(s1.Value1),"hello"); 
    memset(buf1, 0, 60);
    memcpy(buf1, &s1, sizeof(s1));//将结构体1送入char数组,进行发送    


    return 1;
}


//消息发送函数  
int Node::TransmitMessage(int ParticipatorID, char *StructBuffer, int BufferLength)
{
    char SendBuffer[1024];
    memset(SendBuffer, 0, 1024);//将消息放入特定长度的缓冲区进行发送  
    memcpy(SendBuffer, StructBuffer, BufferLength);
    SOCKET SendSocket = ::socket(AF_INET, SOCK_DGRAM, 0);
    SOCKADDR_IN bcast;
    bcast.sin_family = AF_INET;
    bcast.sin_port = htons(10000 + ParticipatorID);//目标地址的端口号设置  
    bcast.sin_addr.S_un.S_addr = ::inet_addr("127.0.0.1");//目标地址设置  
                                                          //发送数据  
    if (sendto(SendSocket, SendBuffer, 1024, 0, (sockaddr*)&bcast, sizeof(bcast)) == SOCKET_ERROR)
    {
        cout << "数据发送错误" << endl;
        return 0;
    }
    return 1;
}


// udp_line_server.cpp : 定义控制台应用程序的入口点。
//


#include "stdafx.h"


#include "Node.h"  
int main()
{
    int ret;
    WSADATA wsa;
    //初始化套接字DLL  
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        cout << "套接字初始化失败!" << endl;
    }
    char MessageBuffer[1024];
    Node A;
    MSG ThreadMessage;
    A.StartMyThread();//启动新线程  
    ret = A.SendStruct();//发送结构体数据  


    while (1)//循环接收线程的消息  
    {
        if (PeekMessage(&ThreadMessage, NULL, 0, 0, PM_REMOVE))//搜集线程的消息  
        {
            if (ThreadMessage.message == WM_PARTY_MSG)//判断消息类型  
            {
                memcpy(MessageBuffer, (char *)ThreadMessage.lParam, 1024);//提取需要处理的网络信息  
                ret = A.DealMessage(MessageBuffer, 1024);//处理网络信息  
                if (ret == 1)
                { 
                    A.SendStruct();
                    break;
                }
                   
            }
        }
    }


    system("pause");
    return TRUE;
}


#include "stdafx.h"
#include "Node.h"  
const int  MSG_STRUCT1 = 1;
const int MSG_STRUCT2 = 2;
const int MSG_STRUCT3 = 3;
#pragma comment(lib,"ws2_32.lib")
Node::Node()//构造函数  
{
    cout << "***************************************************" << endl;
    cout << "\n该程序主要包括:\n" << endl;
    cout << "1.多线程套接字程序设计框架" << endl;
    cout << "2.利用套接字发送结构体的方法" << endl;
    cout << "3.多个不同结构体的发送和接收" << endl;
    cout << "\n默认设置为:\n" << endl;
    cout << "1.各参与者IP地址为127.0.0.1(环回地址,用于单机测试)" << endl;
    cout << "2.编号为i的参与者端口号为10000+i" << endl;
    cout << "3.编号为1的参与者向编号为2的参与者发送测试数据" << endl;
    cout << "***************************************************" << endl;
    cout << "输入编号:" << endl;
    cin >> ID;
}


Node::~Node()//析构函数  
{


}


void Node::StartMyThread()//启动新线程  
{
    DWORD myMainThreadID = ::GetCurrentThreadId(); //获取当前线程也就是主线程的ID号  
    static ThreadParameter tp;//此处需设置为静态变量  
    tp.MainThreadID = myMainThreadID;
    tp.Port = 10000 + ID;//端口号初始化  
    HANDLE hThread = CreateThread(NULL, 0, StartAcceptThread, (LPVOID)&tp, 0, NULL);//创建新线程  
    CloseHandle(hThread);
}


DWORD WINAPI Node::StartAcceptThread(LPVOID lpData)//线程的启动函数,用来循环接收来自参与者发来的轮消息  
{
    ThreadParameter tp = *((ThreadParameter *)lpData);
    char RecveBuffer[20][4096];//为了保证,消息能够被安全处理,也就是在消息被线程处理之前,保证没有新的消息覆盖,我们定义了一个二维数组,相当于二十个缓冲区,用来接收数据  
    for (int i = 0; i<20; i++)
        memset(RecveBuffer[i], 0, 4096);
    SOCKET RecveSocket = ::socket(AF_INET, SOCK_DGRAM, 0);//创建UDP套接字  
    SOCKADDR_IN sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    sin.sin_port = ::htons(tp.Port);//端口号设置  
    if (::bind(RecveSocket, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)//地址与套接字绑定  
    {
        cout << "套接字绑定错误!" << endl;
        return 0;
    }
    /*以下是循环接收网络数据部分,在通信量大的时候,
    **可能数据不能及时传至主线程,而造成信息的覆盖
    **所以此处设置了一个二位数组RecveBuffer,相当于
    **20个长度为4096的缓冲区,可以适应通信量较大的情况。
    */
    SOCKADDR_IN addrRemote;
    int nLen = sizeof(addrRemote);
    int recvcount = 0;
    while (true)
    {
        if (recvcount == 20)
            recvcount = 0;
        //套接字接收函数  
        int nRet = ::recvfrom(RecveSocket, RecveBuffer[recvcount], 4096, 0, (sockaddr*)&addrRemote, &nLen);
        if (nRet == SOCKET_ERROR)
        {
            cout << "接受错误!" << endl;
            return 0;
        }
        if (nRet > 0)
        {
            //收到网络数据以后向主线程发送消息,并将数据交由主线程处理  
            if (PostThreadMessage(tp.MainThreadID, WM_PARTY_MSG, 0, (LPARAM)RecveBuffer[recvcount]) == 0)
            {
                cout << "向主线程发送消息失败" << GetLastError() << endl;
            }
            recvcount++;
        }
    }
    return 1;
}


//消息处理函数,该函数用处理不同的网络数据,对其进行分类响应  
int Node::DealMessage(char *MyMessage, int MessageLength)
{
    int StrcutFlag;
    //获取网络数据的标志位,根据不同的标志位进行不同的处理  
    memcpy(&StrcutFlag, MyMessage, sizeof(StrcutFlag));
    if (StrcutFlag == MSG_STRUCT1)//结构体1的数据  
    {
        m_struct1 ms1;
        memcpy(&ms1, MyMessage, sizeof(ms1));//取出结构体1  
        if (strstr(ms1.Value1, "hello"))
        {
            return 1;
        }
        //cout << ms1.Value1 << endl;
    }
    return 1;
}


//结构体发送函数  
int Node::SendStruct()
{
    char buf1[60], buf2[60], buf3[60];
    if (ID != 1)
        return 0;
    //定义三种类型的结构体  
    m_struct1 s1;
    m_struct2 s2;
    m_struct3 s3;
    //对不同的结构体进行不同的赋值  
    s1.flag = 1;
    strcpy_s(s1.Value1, "recvd");
    TransmitMessage(2, buf1, sizeof(s1));//发送结构体1  
    return 1;   
}


//消息发送函数  
int Node::TransmitMessage(int ParticipatorID, char *StructBuffer, int BufferLength)
{
    char SendBuffer[1024];
    memset(SendBuffer, 0, 1024);//将消息放入特定长度的缓冲区进行发送  
    memcpy(SendBuffer, StructBuffer, BufferLength);
    SOCKET SendSocket = ::socket(AF_INET, SOCK_DGRAM, 0);
    SOCKADDR_IN bcast;
    bcast.sin_family = AF_INET;
    bcast.sin_port = htons(10000 + ParticipatorID);//目标地址的端口号设置  
    bcast.sin_addr.S_un.S_addr = ::inet_addr("127.0.0.1");//目标地址设置  
                                                          //发送数据  
    if (sendto(SendSocket, SendBuffer, 1024, 0, (sockaddr*)&bcast, sizeof(bcast)) == SOCKET_ERROR)
    {
        cout << "数据发送错误" << endl;
        return 0;
    }
    return 1;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值