TskingConsoleServer 第2版 2019/06/09

Published

功能:

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 文件