发表于:2008-01-18 11:13:00
楼主
开发OPC服务器需要开发相应的通信程序(搞电气的人习惯喊驱动)
在这里谈一下比较常用的Modbus TCP现场总线协议的开发。
在这里我采用API的方式使用Socket,
第一步:添加头文件和库文件
#include "winsock2.h"
wsock32.lib
第二步:用WSAStartup函数初始化Winsock.dll
int errCode;
m_wVersionRequested = MAKEWORD( 1, 1 );
errCode = WSAStartup( m_wVersionRequested, &(m_WSAData) );
if ( errCode != 0 )
{
m_wsaInitialized = FALSE;
}
else
{
m_wsaInitialized = TRUE;
}
说明:由于Winsock目前有两个版本:2.2和1.1。1.1版本的DLL为Winsock.dll,而2.2版本的DLL则为Wsock32.dll。
WSAStartup函数函数原型为: int WSAStartup (WORD wVersionRequested,LPWSADATA lpWSAData); 其第一个参数为Winsock版本!低字节为主版本,高字节为副版本!由于目前Winsock有两个版本:1.1和2.2,因此该参数可以是0x101或0x202;第2个参数是一个WSADATA结构,用于接收函数的返回信息!WSAStartup函数调用成功会返回0,否则返回非0值!
第三步:创建Socket套接字
m_socket = socket(PF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == m_socket) // recommended NT error check
{
GetSockError(errStr);
sprintf(debugStr, "Cannot create Link socket :%s",errStr);
OutputDebugString(debugStr);
return FALSE;
}
创建套接字有两个函数,socket和WSASocket,前者是标准的Socket函数,而后者是微软对Socket的扩展函数。socket函数有3个参数,第一个是指定通信发生的区域,在UNIX下有AF_UNIX、AF_INET、AF_NS等,而在Winsock1.1下只支持AF_INET,到了2.2则添了AF_IRDA(红外线通信)、AF_ATM(异步网络通信)、AF_NS、AF_IPX等;第2个参数是套接字的类型,在AF_INET地址族下,有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW三种套接字类型。SOCK_STREAM也就是通常所说的TCP,而SOCK_DGRAM则是通常所说的UDP,而SOCK_RAW则是用于提供一些较低级的控制的;第3个参数依赖于第2个参数,用于指定套接字所用的特定协议,设为0表示使用默认的协议。socket函数调用成功返回一个套接字描述符,错误则返回SOCKET_ERROR。
我们是在Windows操作系统下写Modbus TCP Client程序,所以在这里的第一个参数是PF_INET,第二个参数是SOCK_STREAM。
第四步:连接服务器
if(connect(m_socket, (LPSOCKADDR)&m_destSockaddr_in, sizeof(SOCKADDR)) < 0)
{
shutdown(m_socket, SD_BOTH);
closesocket(m_socket);
m_socket = INVALID_SOCKET;
return FALSE;
}
connect函数有3个参数,第1个是连接所使用的套接字描述符,第2个参数是一个sockaddr结构,sockaddr结构是一个通用的结构,它只是简单地定义了一个字节数组。在TCP/IP下一般将其解释为sockaddr_in结构,第3个参数则是该结构的长度,一般用sizeof函数来取得。connect函数调用失败则返回SOCKET_ERROR。
第五步:发送和接收数据
dwWritten = send(m_socket, (CHAR*)msg->m_buffer, msg->m_wLength, 0);
send函数有4个参数:
第一个是发送操作所用的套接字描述符;
第二个是所要发送的数据缓冲区的地址为CHAR类型,至于其它类型的数据可以用强制类型转换CHAR。在接收端再用强制类型转换转换回来;
第三个参数是所发送的缓冲区的大小,也就是所要发送的字节数;
第四个参数是一个附加标志,如果对所发送的数据没特殊要求,则直接设为0。
CMODMessage msg;
msg.Add(0x00);
msg.Add(0x00);
msg.Add(0x00);
msg.Add(0x00);
msg.Add(0x00);
msg.Add(0x06);
msg.Add(0x01);
msg.Add(0x02);
msg.Add(0x00);
msg.Add(0x00);
msg.Add(0x00);
msg.Add(0x0A);
针对Modbus TCP,发送之前用msg来组合发送的数据帧。这里是读02 Input Status寄存器的0X0000到0X000A的数据。
dwRead = recv(m_socket, &input, RX_QUEU, 0);
recv函数的参数也是4个,其涵义与send函数差不多。只是其第二个参数是指向用于接收数据的缓冲区的地址。
msg->m_buffer[msg->m_wLength++] = input;
第六步:断开连接 用closesocket
第七步:退出程序
if(m_wsaInitialized)
{
WSACleanup();
}