📄 rcpserver.cpp
字号:
// RCPServer.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "resource.h"
//#include "WSAErrorString.h"
//#include "id_handler.h"
// 时间的限制,以分钟为单位
#define TIME_LIMIT 10
// 服务器端主窗口的类名
#define SERVER_WINDOW_CLASSNAME "drm_rca_server_window_class"
// 服务器端主窗口的标题名
#define SERVER_WINDOW_CAPTIONNAME "RCA 服务器"
// 客户端主窗口的类名
#define CLIENT_WINDOW_CLASSNAME "drm_rca_client_window_class"
// 用于创建命名信标对象,控制服务器同一时刻最多只能运行两个实例
#define RCA_RUNNING_CONTROL_SEMAPHORE_NAME "rca_running_semphore_control"
// 当前软件的版本号, 语法 MAKEWORD( 次版本号, 主版本号)
#define CURRENT_VERSION MAKEWORD( 0, 2 )
// 自定义版本更新消息, WPARAM 为版本验证值,LPARAM 为传送消息的线程的 ID
// WM_VERSIONUPDATE 消息有两个类型, 一种为
#define WM_VERSIONUPDATE (WM_USER +3)
#define VERSION_HIGH 1
#define VERSION_LOW 2
#define VERSION_EQUAL 3
// 自定义消息 假如正在工作的 RCA 版本较低, 一个更高版本的 RCA 将会通知低版本的退出
// 然后高版本的将等待低版本的传递 WM_OLDVERSIONEXIT 消息,以表示低版本的已经退出
// 这时高版本的将可以继续运行
#define WM_OLDVERSIONEXIT (WM_USER +4)
#define WM_RCASHELLICON (WM_USER +5)
WORD winsock_version = MAKEWORD(2, 2);
WSADATA wsadata;
///////////////////////////
//RCAInit 函数初始化的变量
CHAR g_pszOS[6]; // OS version info
CHAR g_pszComputerName[MAX_COMPUTERNAME_LENGTH+1]; // computer Name
CHAR g_pszUserName[UNLEN +1]; // current thread's user name
CHAR* g_pszSystemDir; // system directory
CHAR* g_pszWindowsDir; // windows setup directory
HINSTANCE g_hInstance;
OSVERSIONINFO g_SysVer; // OS version struct
MEMORYSTATUS g_MemoryStatus; // memory status struct
BOOL g_bCanAcceptEx; // 初始化时确定的布尔变量, 说明操作系统是否支持
// AcceptEx函数
HWND g_hWnd; // main window handle;
NOTIFYICONDATA g_nid;
HICON g_hBig;
HICON g_hSmall;
HMENU g_hShellIconMenu;
HANDLE g_hRunningControlSemaphore;
DWORD g_dwAnotherRCAVertionUpdateThreadId;
BOOL g_bMustExit = FALSE;
HANDLE g_hExitEvent;
//////////////////////////////////////////////
//
// 服务开始使用的全局变量
//
HANDLE g_hServiceMainThreadHandle = NULL;
DWORD g_dwServiceMainThreadID = NULL;
BOOL g_bServiceMainThreadMustExit = FALSE;
SOCKET g_sockService = NULL;
SOCKADDR_IN g_addrServer; // 服务器地址描述
WORD g_wServerPort; // 服务器端口号, 网络字节顺序
///////////////////////////////////////////////
//
// 有关安全方面的全局变量
//
BOOL g_bAllAccess = TRUE;
///////////////////////////////////////////////////
//
// 函数原型声明
//
BOOL WINAPI RCAInit();
BOOL WINAPI RCAUninit();
BOOL WINAPI CanGoon( DWORD * result );
void WINAPI OutputAddr( unsigned int uIP );
LRESULT CALLBACK MainWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
DWORD WINAPI RCAService( PVOID pVoid );
BOOL WINAPI ServiceMainThreadStartup();
BOOL WINAPI ServiceInit();
BOOL WINAPI ServiceUninit();
BOOL WINAPI AccessControl( RCAREQUESTHEADER * pRequestHead, DWORD dwRemoteAddress, WORD wPort );
DWORD WINAPI RequestHandle( SOCKET accept, DWORD dwRemoteAddress );
BOOL WINAPI GetRCARequestIDHandler( DWORD dwRequestID, RCAREQUESTIDHANDLER * pHandler);
/////////////////////////////////////////////////
//
// 通过客户端请求的操作 ID , 查找相应的 ID 处理函数
//
//
BOOL WINAPI GetRCARequestIDHandler( DWORD dwRequestID, RCAREQUESTIDHANDLER * pHandler)
{
switch ( dwRequestID )
{
case 0x10: // 16 进制 10 号, 10 进制 16 号, 创建进程执行
*pHandler = RCAID_0X10_HANDLER;
break;
case 0x20:
*pHandler = RCAID_0X20_HANDLER;
break;
case 0x40:
*pHandler = RCAID_0X40_HANDLER;
break;
case 0x41:
*pHandler = RCAID_0X41_HANDLER;
break;
case 0x110:
*pHandler = RCAID_0X110_HANDLER;
break;
default:
return FALSE;
}
return TRUE;
}
//////////////////////////////////////////////////////
//
// 说明:
// 一个客户端的请求已经到来, 先分析请求是否为 RCA 请求
// 请求 如果是一个 RCA 请求, 分两步处理:
// 1. 对请求的访问控制, 这将涉及到发出请求的客户端 IP 地址,
// 请求的操作 ID. 这些由 AccessControl 函数完成, 如果 AccessControl
// 返回 TRUE , 则允许执行这个请求, 返回 FALSE, 将拒绝请求.
//
// 2. 如果允许执行请求, 将调用 ID 分流模块,
// 以将请求正确的传递到相应的处理程序
//
DWORD WINAPI RequestHandle( PVOID p )
{
RCAREQUESTHANDLEDATA * pData = (RCAREQUESTHANDLEDATA *)p;
SOCKET sockRemote = pData->sockRemote;
HANDLE hEventArray[2];
char headBuffer[40];
PRCAREQUESTHEADER pRCAHead = NULL;
int ret;
int nBytes = sizeof(RCAREQUESTHEADER);
int nLeft = nBytes;
int i = 0;
BOOL bExit = FALSE;
hEventArray[0] = CreateEvent( NULL , FALSE, FALSE, NULL );
hEventArray[1] = g_hExitEvent;
if( hEventArray[0] == NULL )
goto end_request_handle;
ret = WSAEventSelect( sockRemote, hEventArray[0], FD_READ | FD_WRITE | FD_CLOSE );
if( ret != 0 )
{
DEBUGOUTPUT1( WSAERRORSTRING );
exit(0);
}
BOOL bRecv = TRUE;
while( 1 )
{
ret = RCARecv_EventSelectIO( sockRemote, hEventArray, headBuffer, sizeof(RCAREQUESTHEADER));
if( ret != sizeof(RCAREQUESTHEADER) )
break;
pRCAHead = (RCAREQUESTHEADER*)headBuffer;
// 确定前四个字节为一个 ANSI C 字符串 "RCA\0"
if( 0 != lstrcmp( pRCAHead ->rcaID, "RCA" ) )
break;
// 确保版本支持, 当前只支持 1.0 的版本
// 保证协议版本在 1.0 或 1.0 以下
if( pRCAHead->wMajorVersion > 1 )
break;
if( (pRCAHead->wMajorVersion == 1) && (pRCAHead->wMinorVersion!=0) )
break;
/////////////////////////////////////
//
// 至此验证完毕. 大致上符合一个 RCA 的请求
// 现在将调用 AccessControl 函数, 确定此请求是否接受
if( !AccessControl( pRCAHead, pData->dwRemoteAddress, (WORD)pData->dwRemotePort ) )
break;
RCAREQUESTIDHANDLER pFunc; // ID 处理函数的指针
//nBytes = pRCAHead->requestBytes - 40; // 需要接收的字节数, 为请求头中 requestBytes 字段 减去 40
if( GetRCARequestIDHandler( pRCAHead->dwRequestID, &pFunc) )
{
if( TRUE == (*pFunc)( sockRemote , pData, pRCAHead, hEventArray) )
{
nLeft = sizeof(RCAREQUESTHEADER); // 重置接收参数
i = 0; // 重置接收参数
continue; // 继续接收下一个请求
}
else
break; // 结束请求处理
// 省略了一些..... 以后加入
}
}
// 结束请求并释放资源
end_request_handle:
if( hEventArray[0] != NULL )
CloseHandle( hEventArray[0] );
// shutdown( pData->sockRemote, SD_BOTH );
closesocket( pData->sockRemote );
free( p ); // 释放
return 0;
}
//////////////////////////////////////////////////////
//
// 访问控制的实现函数
//
BOOL WINAPI AccessControl( RCAREQUESTHEADER * pRequestHead, DWORD dwRemoteAddress, WORD wPort )
{
// 现在不实现. 当前版本将接受所有的请求
return TRUE;
}
////////////////////////////////////////////////////
//
// 当监听服务线程结束时必须调用的清除资源的操作
BOOL WINAPI ServiceUninit()
{
int ret;
closesocket( g_sockService );
ret = WSACleanup();
if ( ret != 0 )
{
DEBUGOUTPUT1( WSAERRORSTRING );
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////
//
// 监听服务线程需要的初始化操作
//
BOOL WINAPI ServiceInit()
{
char str[256];
str[0] = '0';
int ret;
ret = WSAStartup( winsock_version, &wsadata );
if( ret != 0 )
{
DEBUGOUTPUT1( WSAERRORSTRING );
return FALSE;
}
g_sockService = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if( g_sockService == INVALID_SOCKET )
{
DEBUGOUTPUT1( WSAERRORSTRING );
return FALSE;
}
g_wServerPort = htons( 4813);
g_addrServer.sin_family = AF_INET;
g_addrServer.sin_port = g_wServerPort; // 4813
g_addrServer.sin_addr.S_un.S_addr = htonl( INADDR_ANY ); // 监听任何地址
ret = bind( g_sockService, (SOCKADDR*)&g_addrServer, sizeof( g_addrServer ) );
if( ret != 0 )
{
DEBUGOUTPUT2( WSAERRORSTRING, "无法将套接字绑定到本地地址" );
closesocket( g_sockService );
return FALSE;
}
return TRUE;
}
////////////////////////////
//
// 启动主服务线程
//
// 参数: 无
//
// 返回值: TRUE --- 服务线程已启动
// FALSE --- 服务线程无法启动
//
BOOL WINAPI ServiceMainThreadStartup()
{
g_hServiceMainThreadHandle =
CreateThread(
NULL, // 默认的安全属性
0, // 默认的堆栈大小
RCAService, // 线程函数是 RCAService
NULL, // 无参数
CREATE_SUSPENDED, // 线程创建后将其挂起
&g_dwServiceMainThreadID // 将线程 ID 保存在全局变量 g_dwServiceMainThreadID 中
);
if( g_hServiceMainThreadHandle == 0 )
return FALSE;
else
return TRUE;
}
DWORD WINAPI RCAService( PVOID pVoid )
{
int ret;
static DWORD first = GetTickCount();
static DWORD last;
first = GetTickCount();
if( listen( ::g_sockService, 64 ) != 0 )
{
PostMessage( g_hWnd, WM_DESTROY, 0, 0 );
return 0;
}
SOCKET sockAccept;
HANDLE hEventArray[2];
SOCKADDR_IN clientAddr;
int addrLen;
DWORD dwTempThreadID;
DWORD dwWaitResult;
HANDLE hTemp;
WSANETWORKEVENTS networkEvents;
addrLen = sizeof( clientAddr );
// 创建事件对象, 并将监听插座置为异步模式, 此异步模型为异步事件模型
hEventArray[0] = CreateEvent( NULL, FALSE, FALSE, NULL );
ret = WSAEventSelect( g_sockService, hEventArray[0], FD_ACCEPT );
if( ret != 0 )
{
DEBUGOUTPUT1( WSAERRORSTRING );
exit(0); // 危险代码 !
}
hEventArray[1] = g_hExitEvent;
while( 1 )
{
//服务主线程开始对指定端口号进行监听,最大连接缓冲 64 个
sockAccept = accept( g_sockService , (SOCKADDR*)&clientAddr, &addrLen );
if( sockAccept == INVALID_SOCKET )
{
if( WSAEWOULDBLOCK != WSAGetLastError() )
{
DEBUGOUTPUT1( WSAERRORSTRING );
exit(0);
}
} else
{
// 注: 这里必须在堆上分配内存.
// 一个很常见的编程错误就是给一个刚创建的线程传递在栈上分配的数据
RCAREQUESTHANDLEDATA * pData =
(RCAREQUESTHANDLEDATA*) new BYTE[sizeof(RCAREQUESTHANDLEDATA)];
if( pData == NULL ) // 内存不足, 很少见的情况
{
closesocket( sockAccept );
continue;
}
pData->sockRemote = sockAccept;
pData->dwRemoteAddress = clientAddr.sin_addr.S_un.S_addr;
pData->dwRemotePort = clientAddr.sin_port;
hTemp = CreateThread (
NULL,
0,
RequestHandle,
pData,
0,
&dwTempThreadID);
if( hTemp != 0 )
CloseHandle( hTemp );
continue;
}
if ( (GetTickCount() - first) > TIME_LIMIT * 60 * 1000 )
{
PostMessage(g_hWnd, WM_DESTROY, 0, 0 );
return 0;
}
dwWaitResult = WaitForMultipleObjects( 2, hEventArray, FALSE, INFINITE );
// 一个网络事件发生后, 用 WSAEnumNetworkEvents 列举网络事件
if( dwWaitResult == (WAIT_OBJECT_0 + 1) )
{
CloseHandle( hEventArray[0] );
return 0;
}
if( dwWaitResult == WAIT_OBJECT_0 )
{
ret = WSAEnumNetworkEvents( g_sockService, hEventArray[0], &networkEvents );
if( networkEvents.lNetworkEvents & FD_ACCEPT )
{
// FD_ACCEPT 事件没产生错误,将再次 accept( ... ) 以接受连接
if( networkEvents.iErrorCode[ FD_ACCEPT_BIT ] == 0 )
{
// 接受连接
continue;
}
else
{
DEBUGOUTPUT1( WSAERRORSTRING );
}
}
}
}// service while
CloseHandle( hEventArray[0] );
return 0;
}
VOID WINAPI OnShellIconMessage( HWND hwnd, WPARAM w, LPARAM l )
{
static int x;
static int y;
POINT p;
// 筛选图标 ID
switch( w )
{
case 1: // RCA SERVER ICON
// 筛选消息
switch( l )
{
case WM_RBUTTONUP:
GetCursorPos( &p );
// 假如选中了"退出"命令
if( 2 == TrackPopupMenuEx( g_hShellIconMenu,
TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD |
TPM_BOTTOMALIGN | TPM_RIGHTALIGN, p.x, p.y ,hwnd, NULL ) )
DestroyWindow( hwnd );
break;
}
break;
}
}
LRESULT CALLBACK MainWndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_RCASHELLICON:
OnShellIconMessage( hwnd, wParam, lParam );
return 0;
case WM_CREATE:
g_nid.cbSize = sizeof(NOTIFYICONDATA );
g_nid.hIcon = g_hSmall;
g_nid.hWnd = hwnd;
lstrcpy( g_nid.szTip, "RCA SERVER v0.8 beta \n使用限时:10分 \n端口: 4813" );
g_nid.uCallbackMessage = (WM_RCASHELLICON);
g_nid.uID = 1;
g_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -