关键字 WINSOCK BYTE
原作者姓名 戚高
介绍
在中小型电站系统开发中,如果不采用组态软件开发,我们可以采用VC+ACCESS模式进行。ACCESS可以用于LOCAL单机版程序运行。同事我们也可以用VC+SQL模式实现。一般ACCESS数据库小,操作灵活。在很多的小型系统开发中是最常用的数据库。
但是有些系统开发中LOCAL可能组成局域网进行数据共享,这时我们需要编写TCP/IP程序进行数据的共享。控制系统中一般传递数据为BYTE,下面的实例代码以BYTE讲解初级的TCP/IP通讯原理和基本实现。
正文
在局域网工业控制系统通讯中常要求采用的是不管是服务器还是客户端掉线,当机器重启后要求能够自动连接到相应的点然后进行通讯而不要用户手动启动某些操作过程等。这里实例以BYTE类型数据进行通讯。因为在控制系统中传递的常常是数字量或者模拟量节点数据。这些数据我们一般都以BYTE类型数据传送。然后传递方和接受方采用指定的相应的通讯协议规约进行解包然后进行数据分析处理等。
以前在WWW.VCKBASE.COM上面看过一篇关于不间断TCP/IP通讯的文章,中间传输的是char*类型数据。但是如果传输BYTE数据,当出现数据位为0的时候就会出现问题。而客户端和服务器端多线程程序在满足某个节点退出的情况下重启不能自动连接的问题。经过修改能够达到基本的要求。
首先我们设计两个界面,一个服务器和一个客户端界面。如下:
服务器端每隔300毫秒更新界面数字量和模拟量节点的内容,然后发送到客户端进行数据刷新显示
客户端每隔100毫秒更新界面数字量和模拟量节点内容。然后发送到服务器端进行数据刷新显示
一:服务器端程序采用多线程监听端口,当出现客户端的新连接时满足连接条件建立连接,并保存相应的SOCKET,然后自动开辟线程通道进行通讯。
//服务器端侦听线程
UINT _ListenThread(LPVOID param)
{
CTestServerDlg* pDlg = (CTestServerDlg*)param;
int ret = pDlg->m_Server.InitAndListen(pDlg->m_Port);
if(ret == 0)
{
pDlg->ResetListMsg(-1);
ReceiveThread_S = ::AfxBeginThread(_ReceiveThread_S,pDlg);
}
//不断侦听
WaitForSingleObject(pDlg->hPrepareEvent,0);
ListenThread = ::AfxBeginThread(_ListenThread,pDlg);
return 0;
}
//服务器端接收数据线程,服务器端每个"接收套接字"各占用一个线程。即如果有N个客户端,则有N个接收线程
UINT _ReceiveThread_S(LPVOID param)
{
CTestServerDlg* pDlg=(CTestServerDlg*)param;
char buff[10*1024]={0};
BYTE bInbuff[100];
for(int j=0; j<100; j++)
bInbuff[j] = 0x00;
int i = pDlg->m_Server.curr-1;
ResetEvent(pDlg->hPrepareEvent);
for(;;)
{
int ret = pDlg->m_Server.ReceiveMsg_S(buff,i);
if(ret == 0)
{
for(int i=0; i<100; i++)
bInbuff[i] = (BYTE)buff[i];
// 解包
pDlg->DataProcess_ClientPaket(bInbuff);
}
else if(ret == WSAECONNRESET)//如果客户端退出,服务器端退出相应的接收线程。
{
pDlg->m_Server.curr --;
pDlg->ResetListMsg(i);
break;
}
}
ReceiveThread_S = NULL;
return 0;
}
void CTestServerDlg::DataProcess_ClientPaket(BYTE m_Inbuff[])
{
服务器端解客户端传递报文程序。
}
如果在服务器程序连接客户端程序比较多的情况下,如果解包需要占用很长的时间,我们需要采用线程互斥的方法进行处理,即当一个线程进入解包程序的时候挂起别的线程,等当前线程处理完成激活别的线程进行处理。
我们可以修改成如下:
void CTestServerDlg::DataProcess_ClientPaket(BYTE m_Inbuff[])
{
//进入处理的时候其余线程被挂起,知道原来线程完成释放才被唤醒执行
CriticalSection.Lock();
..................
相应的解包处理操作
CriticalSection.Unlock();//释放互斥量
}
二:客户端处理程序
//客户端连接线程
UINT _ConnectThread(LPVOID param)
{
CString ip;
CTestClientDlg* pDlg = (CTestClientDlg*)param;
pDlg->m_sIP.GetWindowText(ip);
int ret = pDlg->m_Client.ConnectServer((LPSTR)(LPCTSTR)ip,pDlg->m_Port);
//如果连接成功,客户端开始接收线程。
if(ret==0)
{
pDlg->bIsConnected = TRUE;
ReceiveThread_C = ::AfxBeginThread(_ReceiveThread_C,param);
}
//不断连接
while(pDlg->bIsConnected)
{
Sleep(100);
}
ConnectThread = ::AfxBeginThread(_ConnectThread,pDlg);
return 0;
}
//客户端接收数据线程
UINT _ReceiveThread_C(LPVOID param)
{
CTestClientDlg* pDlg=(CTestClientDlg*)param;
char buff[1024*10]={0};
BYTE bInbuff[100];
for(int j=0; j<100; j++)
bInbuff[j] = 0x00;
ResetEvent(pDlg->hPrepareEvent);
for(;;)
{
int ret=pDlg->m_Client.ReceiveMsg_C(buff);
if(ret==0)
{
for(int i=0; i<100; i++)
bInbuff[i] = (BYTE)buff[i];
// 解包
pDlg->DataProcess_ServerPaket(bInbuff);
}
if(ret==WSAECONNRESET)
{
pDlg->bIsConnected = FALSE;
pDlg->m_Client.m_hSocket = NULL;
break;
}
}
ReceiveThread_C=NULL;
return 0;
}
以上程序在windows2000+vc6中测试通过。同时可以作为检测网络是否通畅的一个工具。由于在系统运用中使用固定服务器IP地址。没有写成灵活的方式,各位可以根据进行自行修改。
如果有什么问题可以和我联系:13975102873@hnmcc.com
正文完
附件: