前段时间做打印机的打印程序,碰到了与对端连接问题,要实现的效果是不管是服务端先起来,还是打印机先起来,都要能握手成功,从而进行后面的信息传递,而打印出来。采用多线程+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;
}