功能:
1.实现登陆回应包
2.客户端连接上后循环发送、接收
3.服务器可以接受一个客户端可多次连接、断开服务
未实现功能:
部分条件判断未完善。
// Server.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
// Server.cpp : 定义控制台应用程序的入口点。
//
#include <stdio.h> //用于printf等函数的调用
#include <iostream>
#include "winsock2.h" //Socket的函数调用
#pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll。C语言引用其他类库时,除了.h文件外,还要加载对应的lib文件
#include <iostream>
using namespace std;
#pragma pack(1) //作用:C编译器将按照1个字节对齐。
#define TW_MAGIC 0xFDFDFDFD
#define TW_MAGIC_BYTE 0xFD
#define TW_LENGTH_LEN 8
//登陆结构
typedef struct _tw_header_t {
DWORD m_magic; // 0xFDFDFDFD
char m_length[TW_LENGTH_LEN];
char m_end; // always 0
} TW_HEADER;
typedef struct _tw_login_t {
TW_HEADER m_header; //
BYTE m_tag; // 0x0A
WORD m_name_len;
char m_data[256]; // (NAME)(WORD m_passwd_len)(PASSWD)
} TW_LOGIN;
typedef struct _tw_ans_t {
TW_HEADER m_header; //
BYTE m_tag1; // second data type
BYTE m_tag2; // data type
BYTE m_serial; // request serial, max 0x7f
BYTE m_reserved; // always 0x00
SHORT m_size; // request data's size
} TW_ANS;
typedef struct tagSTOCK_STRUCTEx {
BYTE m_type; // stock's type, see enum StockType
char m_code[6]; // stock code
} STOCK_STRUCTEx, * pSTOCK_STRUCTEx;
typedef STOCK_STRUCTEx TW_STOCK;
typedef struct _tw_ask_t {
TW_HEADER m_header; //
BYTE m_tag1; // second data type
BYTE m_tag2; // data type
BYTE m_serial; // request serial, max 0x7f
BYTE m_reserved; // always 0x00
SHORT m_size; // request data's size
TW_STOCK m_stocks[32]; // max 32 stocks
} TW_ASK;
typedef struct _tw_ans_init_t {
BYTE m_tag; // = 0xfd
CHAR m_name[8];
BYTE m_type;
CHAR m_code[6];
DWORD m_lastclose; // 昨收 0.001
DWORD m_reserved2;
CHAR m_shortname[4];
} TW_ANS_INIT;
typedef struct _tw_ans_report_t {
WORD m_number; // No.
DWORD m_volnow; // 现手(单位为股)
DWORD m_open; // 0.001
DWORD m_high; // 0.001
DWORD m_low; // 0.001
DWORD m_new; // 0.001
DWORD m_volume;
DWORD m_amount;
DWORD m_buy1; // 0.001
DWORD m_buy1vol;
DWORD m_buy2; // 0.001
DWORD m_buy2vol;
DWORD m_buy3; // 0.001
DWORD m_buy3vol;
DWORD m_sell1; // 0.001
DWORD m_sell1vol;
DWORD m_sell2; // 0.001
DWORD m_sell2vol;
DWORD m_sell3; // 0.001
DWORD m_sell3vol;
WORD m_reserved; // = 0x64 0x00
} TW_ANS_REPORT;
#pragma pack() //作用:取消自定义字节对齐方式。
int ConstructLength(TW_HEADER& header, int len)
{
string str = itoa(len, header.m_length, 16);
int nZeros = TW_LENGTH_LEN - str.size();
int i;
for (i = 0; i < nZeros; i++)
for (i = 0; i < TW_LENGTH_LEN; i++)
{
if (i < nZeros)
header.m_length[i] = '0';
else
header.m_length[i] = str[i - nZeros];
}
return len;
}
int main()
{
const int BUF_SIZE = 512;
WSADATA wsd; //WSADATA变量
SOCKET sServer; //服务器套接字
SOCKET sClient; //客户端套接字
SOCKADDR_IN addrServ;; //服务器地址
BYTE buf[BUF_SIZE]; //接收数据缓冲区
char sendBuf[BUF_SIZE];//返回给客户端得数据
int retVal; //返回值
BOOL unknownPacketWithNomagic = TRUE;
//初始化Windows Sockets DLL
WSADATA wsaData;
retVal = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (retVal != NO_ERROR)
{
//初始化Windows Sockets DLL失败
printf("wsastartup failed with error : %d\n", retVal);
//WSAGetLastError()
return FALSE;
}
//创建服务端Socket:创建套接字.
SOCKET servSock = socket(PF_INET, SOCK_STREAM, 0);//socket函数对应于普通文件的打开操作
//绑定套接字
sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));// 每个字节都用0填充
sockAddr.sin_family = PF_INET;//使用IPv4地址
sockAddr.sin_addr.s_addr = INADDR_ANY;//具体的IP地址
sockAddr.sin_port = htons(4999);//端口
bind(servSock, (SOCKADDR*)& sockAddr, sizeof(SOCKADDR));
//监听:进入监听状态
retVal = listen(servSock, 1);
if (SOCKET_ERROR == retVal)
return FALSE;
cout << "listen:4999" << endl;
//等待客户端的连接
cout << "Server succeeded!" << endl;
cout << "Waiting for clients..." << endl;
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
//用户登陆返回的数据结构
BYTE buffer[1024];
BYTE sbuffer[512];
TW_ANS ack;
memset(&ack, 0, sizeof(ack));
ack.m_header.m_magic = TW_MAGIC;
ack.m_tag1 = ack.m_tag2 = 0;
memcpy(sbuffer, &ack, sizeof(ack));
sockaddr_in addrClient;
int len = sizeof(SOCKADDR);
while (1)
{
memset(&addrClient, 0, sizeof(sockaddr_in));
printf("\n等待客户端连接.... \n");
//等待客户端调用connect连接服务器,阻塞在这里,直到有连接进来
SOCKET clntSock = accept(servSock, (SOCKADDR*)& addrClient, &len);
if (INVALID_SOCKET == clntSock)
{
cout << "accept(serverSocket, (SOCKADDR*)&clientAddr, &len) execute failed!!" << endl;
closesocket(servSock); //关闭套接字
WSACleanup(); //释放套接字资源;
return -1;
}
//------登陆回应开始-------
//--------获取当前系统时间----------------------
SYSTEMTIME st;
GetLocalTime(&st);
char sDateTime[30];
sprintf_s(sDateTime, "%4d%02d%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
// 原文:https ://blog.csdn.net/wlff_csdn/article/details/68942243
//--------获取ip地址----------------------
unsigned char* pAddr = (unsigned char*)& addrClient.sin_addr.s_addr;
//-----------打印输出信息 -显示客户端的IP和端口 --------------------------------
printf("%s,Recv from Client [%d.%d.%d.%d : %d] \n", sDateTime, pAddr[0], pAddr[1], pAddr[2], pAddr[3], ntohs(addrClient.sin_port));
retVal = recv(clntSock, (char*)buf, BUF_SIZE, 0);
if (SOCKET_ERROR == retVal)
{
cout << "recv failed!" << endl;
closesocket(servSock); //关闭套接字
closesocket(clntSock); //关闭套接字
WSACleanup(); //释放套接字资源;
return -1;
}
char temp[100]; //传送的字符串
memset(temp, 0, sizeof(temp)); //对该内存段进行清
memcpy(temp, &ack, sizeof(ack)); //把这个结构体中的信息从内存中读入到字符串temp中
//接下来传送temp这个字符串就可以了
printf("---login begin--- \n");
printf("Recv from Client data length [ %d] \n", retVal);
//16进制显示
for (int i = 0; i < retVal; i++)
{
printf(" %02X", (unsigned char)buf[i]);
}
//客户端发送过来的数据,数据头判断
TW_HEADER* pHead = (TW_HEADER*)buf;
if (pHead->m_magic != TW_MAGIC)
{
printf("unknown packet with no magic.\n");
}
// 登录
TW_LOGIN* pLogin = (TW_LOGIN*)buf;
char name[64] = { 0 };
char pass[64] = { 0 };
memcpy(name, pLogin->m_data, pLogin->m_name_len);
WORD passLen = *(WORD*)(pLogin->m_data + pLogin->m_name_len);
memcpy(pass, pLogin->m_data + pLogin->m_name_len + sizeof(WORD), passLen);
printf("login! userName is %s passWord is %s\n", name, pass);
send(clntSock, temp, sizeof(ack), 0);
printf("---login end--- \n");
//------登陆回应完成-------
printf("\n---进入循环收发数据--- \n");
while (true)
{
Sleep(100);
//接收客户端数据
memset(buf, 0, sizeof(buf));// 每个字节都用0填充
retVal = recv(clntSock, (char*)buf, BUF_SIZE, 0);
if (SOCKET_ERROR == retVal)
{
cout << " retVal = recv(clntSock, (char*)buf, BUF_SIZE, 0); retVal =-1,recv failed!" << endl;
break;
}
if (buf[0] == '0')
{
printf(" Client data close \n");
closesocket(clntSock);
break;
}
printf("Recv from Client data length: %d \n", retVal);
//16进制显示
for (int i = 0; i < retVal; i++)
{
printf(" %02X", (unsigned char)buf[i]);
}
//分析请求类型
TW_HEADER* pHead = (TW_HEADER*)buf;
BYTE tag1 = *(BYTE*)(buf + sizeof(TW_HEADER));
BYTE tag2 = *(BYTE*)(buf + sizeof(TW_HEADER) + 1);
if (pHead->m_magic != TW_MAGIC)
{
printf("unknown packet with no magic.\n");
}
else
{
printf("\n decode packet: pHead->m_magic = TW_MAGIC.tag1=%02X,tag2= %02X\n", tag1, tag2);
}
if (tag1 == 0x1 && tag2 == 0x1)
{
unknownPacketWithNomagic = FALSE;
printf("\n请求ask.\n");
TW_ASK* pAsk = (TW_ASK*)buf;
DWORD lastest = pAsk->m_stocks[0].m_type;
printf("ask! local lastest date %d\n", lastest);
size_t dataoffset = 469;
int nTotalLen = dataoffset + sizeof(TW_ANS_INIT) * 2;
char* pbuffer = new char[nTotalLen];
memset(pbuffer, 0, nTotalLen);
(*(DWORD*)(pbuffer + 45)) = 20130214;
TW_ANS* pans = (TW_ANS*)pbuffer;
pans->m_header.m_magic = TW_MAGIC;
ConstructLength(pans->m_header, nTotalLen - sizeof(TW_HEADER));
pans->m_tag1 = 0x2;
pans->m_tag2 = 0x1;
TW_ANS_INIT* pinit = (TW_ANS_INIT*)(pbuffer + dataoffset);
pinit[0].m_tag = TW_MAGIC_BYTE;
sprintf(pinit[0].m_code, "%6.6s", "600000");
sprintf(pinit[0].m_name, "%s", "QQQ");
pinit[1].m_tag = TW_MAGIC_BYTE;
sprintf(pinit[1].m_code, "%6.6s", "600004");
sprintf(pinit[1].m_name, "%s", "PTA05");
send(clntSock, pbuffer, sizeof(pbuffer), 0);
delete[]pbuffer;
}
if (tag1 == 0x0 && tag2 == 0x03) // 请求分笔数据
{
unknownPacketWithNomagic = FALSE;
printf("\n请求分笔数据.\n");
TW_ASK* pAsk = (TW_ASK*)buf;
printf("请求分笔数据 report! serial: %d, 本次请求股票个数 size: %d\n", pAsk->m_serial, pAsk->m_size);
size_t dataoffset = 43;
int nTotalLen = dataoffset + sizeof(TW_ANS_REPORT) * 2;
char* pbuffer = new char[nTotalLen];
memset(pbuffer, 0, nTotalLen);
TW_ANS* pans = (TW_ANS*)pbuffer;
pans->m_header.m_magic = TW_MAGIC;
pans->m_serial = pAsk->m_serial;
ConstructLength(pans->m_header, nTotalLen - sizeof(TW_HEADER));
pans->m_tag1 = 0x0;
pans->m_tag2 = 0x3;
TW_ANS_REPORT* preport = (TW_ANS_REPORT*)(pbuffer + dataoffset);
preport[0].m_new = 12.56;
preport[0].m_buy1 = 12.53;
preport[1].m_new = 45360000;
send(clntSock, pbuffer, sizeof(pbuffer), 0);
delete[]pbuffer;
for (int i = 0; i < pAsk->m_size; i++)
{
printf(" code %6.6s type %d\n",
pAsk->m_stocks[i].m_code,
pAsk->m_stocks[i].m_type);
}
}
if (tag1 == 0x01 && tag2 == 0x04) // 请求分钟数据
{
unknownPacketWithNomagic = FALSE;
printf("\n请求分钟数据.\n");
}
if (tag2 == 0x09) // 请求历史数据
{
unknownPacketWithNomagic = FALSE;
printf("\n请求历史数据.\n");
switch (tag1 & 0xF0)
{
case 0x30:
printf("min5 history asked.\n");
break;
case 0x40:
printf("min15 history asked.\n");
break;
case 0x50:
printf("min30 history asked.\n");
break;
case 0x60:
printf("min60 history asked.\n");
break;
case 0x10:
printf("day history asked.\n");
break;
case 0x80:
printf("week history asked.\n");
break;
case 0x90:
printf("month history asked.\n");
break;
}
if (unknownPacketWithNomagic)
{
printf("\nunknownPacketWithNoMagic.\n");
}
}
}
printf("\n---客户端已断开连接--- \n");
//关闭套接字
closesocket(clntSock);
}
//关闭套接字
closesocket(servSock); //关闭套接字
//终止 DLL 的使用
WSACleanup();
return 0;
//---
//退出
closesocket(sServer); //关闭套接字
closesocket(sClient); //关闭套接字
WSACleanup(); //释放套接字资源;
return 0;
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门提示:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件