/*****************************************************************************************/
/* 作者 耿浩 */
/* 创建时间 2015-11-11 */
/* 最后修改时间 2015-11-12 */
/* */
/* wonderware suitelink协议解析 */
/* opclink在5413端口建立监听 */
/* xxxx表示串的长度-2 */
/* xxxx0180客户端向服务器发送基本信息。 */
/* xxxx0100服务器向客户端确认接收到了基本信息(080001000101000300a5保持不变)。 */
/* xxxx1080客户端向服务器发送标记名。 分两次发送第一次发送一个,第二次发送剩下的。 */
/* xxxx0300服务器向客户端发送。服务器把接收到的标记名的标识返回给客户端。 */
/* xxxx0800服务器向客户端发送的字串。是何意义不懂!每次重连发送的都不一样。 */
/* xxxx0900服务器发送的数据包,实时数据交换的数据包。 */
/* xxxx2440(03002440a5)在服务器和客户端不定时发送,估计是服务器或者客户用来判断是否还在线*/
/* */
/* 不满足下面要求 在字符串处理中会出错 */
/* int 32位 */
/* short 16位 */
/* char 8位 */
/* */
/*****************************************************************************************/
#include "stdafx.h"
#include <Winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable : 4786)
//这个警告的意思就是说, list<string> 的构造生成的 Symbols 太长了,超过了255个字符。
//你其实不用理会这个警告,因为它只会影响到 Debugger ,而不会影响到真实的代码。如果编译一个 Release 版本的话,就会发现这个警告已经完全消失了。
#include <map>
#include <vector>
#include <string>
using namespace std;
SOCKET sockClient;
char * AccessName[] ={"op", "a1", "a2"};
typedef struct ITEMINFO
{
const char *pAccessName; //访问名
string itemName; //项目名称
int key; //索引
int quality; //通讯质量
int exist; //服务器是否有该点
union{ //值
int i;
float r;
bool d;
}value;
} ItemInfo;
typedef vector<string> AccessNameList;
typedef map<int, ItemInfo> ItemList;
AccessNameList g_accessNameList; //访问名列表
ItemList g_itemList; //项目地址列表
//访问名列表 初始化
void InitAccessList(AccessNameList &accessNameList);
//item列表 初始化
void InitItemList(ItemList &itemList);
//客户端发送 应用程序名 例如: opclink
//服务器端 不返回数据
void SendAppName(const char * appName);
//客户发送 基本信息: 访问名 计算机名 用户名 服务器端程序名称 客户端程序名称 系统时间
//除了访问名 其他是啥都没影响
void SendAccessNames(char * AccessNames[], int nSize);
//每次发送的最大字符个数;
const int bufMaxSize = 80;
//客户端向服务器端发送 项目地址
void setBuf_sendItem(char* &buf, ItemInfo &item);
bool RecvItemFinish();
void SendItemName(ItemList &itemList);
void SendItemName2(ItemList &itemList);
void RecvItemName(ItemList &itemList);
//服务器向客户端发送实时数据
void RecvItemData();
void c2w(const unsigned char* cSrc, wchar_t* wDes);
void w2c(const wchar_t* wSrc, unsigned char* cDes);
void ItemInit(const char * AccessName, const char * itemName, ItemInfo & item)
{
//标记名列表 初始化
item.pAccessName = AccessName;
item.itemName = itemName;
item.key = (int)item.itemName.c_str();
item.quality = 0;
item.exist = 0;
memset(&item.value, 0, sizeof(item.value));
}
void InitAccessList(AccessNameList &accessNameList)
{
//访问名列表 初始化
accessNameList.push_back("op");
accessNameList.push_back("a3");
accessNameList.push_back("a2");
}
void InitItemList(AccessNameList &accessNameList, ItemList &itemList)
{
//item列表 初始化
ItemInfo item01;
ItemInit(accessNameList[0].c_str(), "IRandom.Int1", item01);
itemList[item01.key] = item01;
ItemInfo item02;
ItemInit(accessNameList[0].c_str(), "IRandom.Int2", item02);
itemList[item02.key] = item02;
ItemInfo item03;
ItemInit(accessNameList[0].c_str(), "IRandom.Int3", item03);
itemList[item03.key] = item03;
// ItemInfo item04;
// ItemInit(accessNameList[0].c_str(), "IRandom.Int4", item04);
// itemList[item04.key] = item04;
}
int main(int argc, char* argv[])
{
InitAccessList(g_accessNameList);
InitItemList(g_accessNameList, g_itemList);
//SendItemName2(g_itemList);
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(1, 1);
if(WSAStartup(wVersionRequested, &wsaData) != 0){
return 0;
}
sockClient = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("192.168.0.73");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(5413);
int retcon = connect(sockClient, (SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
if(retcon == SOCKET_ERROR){
return 0;
}
SendAppName("opclink");
SendAccessNames(AccessName, 0);
SendItemName(g_itemList);
RecvItemData();
closesocket(sockClient);
WSACleanup();
printf("结束!\n");
return 0;
}
//输入名称不能包括\0 ,有\0肯定错了,因为用来strlen来取长度,可见名称中也不可能包含\0
//MultiByteToWideChar WideCharToMultiByte
//返回值代表长度, 其长度是strlen+1, 其最后一位是0
void c2w(const unsigned char* c, wchar_t* w)
{
int len = strlen((const char *)c);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)c, -1, w, len);
}
//输入名称不能包括\0 ,有\0肯定错了,因为用来wcslen来取长度,不过可见名称中也不可能包含\0
void w2c(const wchar_t* w, unsigned char* c)
{
int len = wcslen(w);
WideCharToMultiByte(CP_ACP, 0, w, -1, (LPSTR)c, len, NULL, 0);
}
void SendAppName(const char * appName)
{
//605fc3b12d05d111bf0800a0c9723e826f70636c696e6b00000000000000000000000000000000000000000000000000
//该数据包发送过去,并不回数
//此数据包的长度是48
//00-15 值固定不变
//16-47 用来存储应用程序名称,应用程序名称最多是32个字符,应用程序名最好别用中文
//如果连接的是opclink, appName = "opclink" 不能出错
int len = strlen(appName);
if (len > 32 || len <= 0)
{
return;
}
const int buflen = 48;
unsigned char buf[buflen];
memset(buf, 0, buflen);
memcpy(buf, "\x60\x5f\xc3\xb1\x2d\x05\xd1\x11\xbf\x08\x00\xa0\xc9\x72\x3e\x82", 16);
memcpy(buf+16, appName, len);
send(sockClient, (const char *)buf, buflen, 0);
}
//客户端向服务器端发送基本信息
void SendAccessNames(char * AccessName[], int nSize)
{
const int MAXSIZE = 1024;
unsigned char buf[MAXSIZE];
memset(buf, 0x00, MAXSIZE);
unsigned char slen = 0;
unsigned char* p_buf = buf;
p_buf += 2; //头2个字节保存的是整串长度-2,这个在最后写入。
*p_buf = 0x01; //标识位: 0180 客户端向服务器发送基本信息
p_buf++;
*p_buf = 0x80; //标识位: 0180 客户端向服务器发送基本信息
p_buf++;
char * ServerAppName = "a"; //opclink 是啥不会有影响
char * ClientAppName = "a"; //view 是啥不会有影响
slen = (unsigned char)strlen((const char *)ServerAppName);
*p_buf = slen; //服务器端应用程序名称 长度
p_buf++;
c2w((unsigned char*)ServerAppName, (wchar_t*)p_buf); //服务器端应用程序名称,以宽字节存储
p_buf += slen*2;
slen = (unsigned char)strlen((const char *)AccessNam