基于VC开发linux服务器
程序指南(V1.0)
2020-07-08
1 概述.... 3
2 背景.... 3
3 采用本方案目的.... 4
4 适用领域.... 4
5 总体思路.... 4
6 开发工具和操作系统要求.... 5
6.1 Windows/VC.. 5
6.2 linux/g++. 5
6.3 unix/g++. 6
7 linux移植工作.... 6
7.1 常用宏定义... 6
7.2 布尔类型常量宏定义... 6
7.3 数据类型定义... 6
7.4 初始化函数... 7
7.5 线程相关移植... 7
7.5.1 类型定义... 7
7.5.1 相关函数... 8
7.6 同步对象... 9
7.6.1 通用宏定义... 9
7.6.2 临界区(CriticalSection) 9
7.6.3 互斥锁(mutex) 11
7.6.4 信号量(Semaphore) 11
7.6.5 事件(Event) 11
7.6.1 单写多读锁(SRWLOCK) 12
7.6.2 等待同步对象(WaitForSingleObject) 14
7.7 关闭句柄(CloseHandle) 14
7.1 文件映射(FileMapping) 14
7.2 字符串函数... 14
7.3 时间函数... 15
7.3.1 数据类型定义.... 15
7.3.2 相关函数移植说明.... 16
7.3.3 线程休眠函数(Sleep). 17
7.3.4 获取系统启动毫秒数(GetTickCount). 17
7.4 socket编程... 18
7.4.1 相关通用宏定义... 18
7.4.1 相关数据类型定义... 18
7.4.2 相关函数移植说明... 18
7.4.3 关于大并发TCP编程.... 20
7.5 读取配置文件函数... 21
7.6 动态库相关函数... 21
7.7 文件/目录兼容... 22
7.7.1 宏定义... 22
7.7.2 相关函数... 22
7.8 系统错误码... 23
8 VC工程源码兼容性调整.... 23
8.1 建议... 23
8.2 准备移植文件... 24
8.3 宏定义... 24
8.1 socket函数调整... 24
8.2 新增释放读写锁... 24
9 本方案的功能限制.... 24
10 本方案的缺点.... 25
11 成功案例.... 25
[if !supportLists]1 [endif] 概述
在此仅把此文献给那些奋斗在一线的c/c++开发人员,希望可以帮到大家。这是作者多年跨平台编程经验的总结,以文档的形式供大家参考。
[if !supportLists]2 [endif] 背景
本人一直基于微软Microsoft Visual C++(以下简称VC)开发后台服务器程序,
由于项目需要,程序需要运行在linux/unix(以下的linux代表Linux和unix)
平台。但是本人已经非常熟悉和依赖VC,不打算再掌握linux下的开发工具进
行开发。 如果采用linux特有的API函数,windows源程序需要进行大量的修
改。在朋友的指点下,采用基于VC开发、调试、测试,然后移植到linux/g++
进行编译运行。随着移植文件的完善和编码兼容性的优化,现在基本上可以做到VC的工程迁移到linux,只需要做很少或不做修改即可编译通过。
[if !supportLists]3 [endif] 采用本方案目的
采用本方案希望达成以下几点目的。
1、充分利用微软公司VC强大的可视化编辑、调试功能。同时可使用配套的版本管理工具
和Visual Assit X开发助手,有助于提高开发过程的自动化和开发效率。
2、实现开发服务器程序跨平台(windows、linux、unix等)、跨开发工具(VC、g++等)。
3、只需在VC下一次编写源码、一次调试、一次测试,然后到其他平台只需要编译、运行
即可。
4、加快代码的编写速度和质量。
5、加快代码调试速度和质量。
6、加快程序乃至整个项目的上线进度。
本方案主要是移植大部分常用的功能函数,无意移植所有的功能函数。首先是有些函数由于操作系统的差异无法移植,或者移植代价太大完全没有必要。
给开发人员提供多一种跨平台项目开发的选择,无意也不可能取代linux/g++开发工具和其他跨平台开发解决方案。
本方案最适合开发后台服务器程序,如果开发人员要应用于其他领域,需要慎重考虑可行性。
[if !supportLists]4 [endif] 适用领域
本文适合以下读者:
1、本文适用读者是面向VC开发人员,他们需要进行linux下进行开发服务器程
序。 采用本方案对VC开发人员技能要求:熟悉VC开发工具、熟悉C/C/++、熟
悉标准库(可选)、熟悉windows核心编程(多线程、线程同步等)、Socket编
程、了解linux和g++基本知识,不要求掌握MFC。
2、现有的windows平台下的VC工程需要移植到linux平台下运行。
3、针对linux/g++ 开发人员也具有参考意义。
采用本方案的领域:后台服务器程序。
[if !supportLists]5 [endif] 总体思路
1、采用VC开发工具进行服务器程序开发,源码中不使用任何MFC的类
库,可以采用跨平台的第三方类库(标准库、Boost库),或采用我方提供的相
关功能类库。开发过程中只采用标准的C函数或者Windows系统API函数。
2、在linux下利用gcc/g++编写移植文件,把windows的API用linux/g++提供的
API进行封装,还有就是把部分只有VC特有的c函数也用gcc/g++进
行封装。
3、利用VC/windows上进行开发、调试、测试。
4、把代码文件复制到linux下,然后在把移植文件也加入到工程中,进行编译。
一般情况下可以编译通过,即使编译过程中有错误,也可以通过调整达到代
码兼容的效果。
7、用gcc/g++进行编译,生成可执行文件,运行可执行文件进行测试。
说明:
1、如果是开发动态库/静态库和上述过程类似。
2、作者会提供linux的2个移植源码文件:PortLinux.cpp、PortLinux.h
[if !supportLists]6 [endif] 开发工具和操作系统要求
[if !supportLists]6.1 [endif] Windows/VC
1、建议采用/VC 2008及以上版本,同时安装Visual
Assist X开发助手。
2、操作系统
1)桌面系统采用64位Win7及以上版本。
2)服务器系统采用Windows
2008x64及以上版本
3、其他
如果只是开发32位程序,建议安装VC6,同时安装Visual Assist X开发助
手。
[if !supportLists]6.2 [endif] linux/g++
作者开发选用RedHat和CentOS 的linux,如果开发人员希望在其他厂商的linux上开发,需要进行进一步的测试
[if !supportLists]6.3 [endif] unix/g++
作者没有在unix下开发的经历,如果开发人员在unix(HP_UX、AIX、Solaris、BSD)下采用本方案,需要进行进一步测试作者提供的移植文件,如果有必要可进行修改和完善。
[if !supportLists]7 [endif] linux移植工作
需要在linux/g++下编写移植文件,作者提供2个移植文件PortLinux.cpp和
PortLinux.h,开发人员需要在自己的源码文件(c、cpp、h)中包含(#include)PortLinux.h文件。
编写移植文件需要熟悉linux/gcc/g++的开发人员协助VC开发人员一起编写,移植文件需要在linux 下编译、调试、测试,后续就可以把移植文件加入到g++工程里进行整体编译。移植工作只需要做一次即可,但是做到可以一劳永逸。
[if !supportLists]7.1 [endif] 常用宏定义
#define INFINITE 0xFFFFFFFF
#define WINAPI
[if !supportLists]7.2 [endif] 布尔类型常量宏定义
#define FALSE 0
#define TRUE 1
[if !supportLists]7.3 [endif] 数据类型定义
typedef int BOOL;
typedef void* HANDLE;
typedef void* POSITION;
typedef void* LPVOID;
typedef void* HINSTANCE;
typedef char* LPCTSTR;
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef int INT;
typedef int INT32;
typedef unsigned int UINT32;
typedef long long int INT64;
typedef unsigned long long int UNIT64;
typedef wchar_t WCHAR;
typedef void* THREAD_RETURN_TYPE;
typedef struct _LARGE_INTEGER
{
longlong int QuadPart;
}LARGE_INTEGER;
[if !supportLists]7.4 [endif] 初始化函数
初始化全局变量:获取操作系统开机时间
InitSysPort();
[if !supportLists]7.5 [endif] 线程相关移植
[if !supportLists]7.5.1 [endif] 类型定义
1、在移植文件中进行数据类型定义LPSECURITY_ATTRIBUTES;
typedef char* LPSECURITY_ATTRIBUTES;
用途:用于封装CreateThread函数。
2、LPTHREAD_START_ROUTINE
extern"C"
{
typedef void*(*LPTHREAD_START_ROUTINE)(void *);
}
用途:用于封装CreateThread函数。
[if !supportLists]7.5.1 [endif] 相关函数
[if !supportLists]7.5.1.1 [endif] CreateThread函数
功能:CreateThread是一种微软在Windows API中提供了建立一个新的线程的
函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对
象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。
函数定义如下:
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
unsignedint dwStackSize,
LPTHREAD_START_ROUTINElpStartAddress,
LPVOIDlpParameter,
unsignedint dwCreationFlags,
unsignedlong* lpThreadId);
函数使用说明:
1、其中第1、5参数在linux/g++没有意义,填写默认参数。
1)第1个参数lpThreadAttributes,赋值为NULL。
2)第5个参数dwCreationFlags,赋值为0。
2、执行成功返回非NULL的句柄。
[if !supportLists]7.5.1.2 [endif] TerminateThread函数
功能:TerminateThread在线程外终止一个线程,用于强制终止线程。
函数声明如下:
BOOLTerminateThread(
HANDLE hThread, DWORDdwExitCode);
bool SetThreadPriority(HANDLE hThread, int
nPriority);
[if !supportLists]7.5.1.3 [endif] GetCurrentThreadId函数
获取当前线程一个唯一的线程标识符。
int GetCurrentThreadId ();
[if !supportLists]7.5.1.4 [endif] SetThreadPriority函数
暂时没有实现。
[if !supportLists]7.5.1.5 [endif] 其他说明
1、不要封装暂停线程(SuspendThread)和恢复线程(ResumeThread)函数,主要是没有必要使
用这2个函数,另外在linux下也没有类似的函数和功能。
[if !supportLists]7.6 [endif] 同步对象
[if !supportLists]7.6.1 [endif] 通用宏定义
#define WAIT_OBJECT_0 00
#define WAIT_TIMEOUT 01
[if !supportLists]7.6.2 [endif] 临界区(CriticalSection)
用于同一个进程内多线程间的互斥。
[if !supportLists]7.6.2.1 [endif] 数据类型定义
typedef pthread_mutex_t CRITICAL_SECTION;
typedef CRITICAL_SECTION*
LPCRITICAL_SECTION;
[if !supportLists]7.6.2.2 [endif] 相关函数移植说明
采用linux的互斥锁进行简单封装即可。
voidInitializeCriticalSection(CRITICAL_SECTION* lpCriticalSection)
{
pthread_mutex_init(lpCriticalSection,NULL);
}
voidEnterCriticalSection(CRITICAL_SECTION* lpCriticalSection)
{
pthread_mutex_lock(lpCriticalSection);
}
BOOLTryEnterCriticalSection(CRITICAL_SECTION* lpCriticalSection)
{
int nret;
nret =pthread_mutex_trylock(lpCriticalSection);
if(nret == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
voidLeaveCriticalSection(CRITICAL_SECTION* lpCriticalSection)
{
pthread_mutex_unlock(lpCriticalSection);
}
void
DeleteCriticalSection(CRITICAL_SECTION* lpCriticalSection)
{
pthread_mutex_destroy(lpCriticalSection);
}
[if !supportLists]7.6.3 [endif] 互斥锁(mutex)
用于同一个进程内多线程间的互斥锁的操作,暂时没有实现多进程间的互斥锁操作。
HANDLECreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner,LPCTSTR lpName);
1、第1个参数lpMutexAttributes调用时填写NULL。
2、第4个参数lpName调用时填写NULL。
bool ReleaseMutex(HANDLEhMutex);
[if !supportLists]7.6.4 [endif] 信号量(Semaphore)
用于同一个进程内多线程间的信号量的操作,暂时没有实现多进程间的信号量操作。
具体实现采用linux的互斥锁(pthread_mutex_t mutex)和条件变量
(pthread_cond_t
cond)组合实现。
HANDLE
CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
intlInitialCount,
intlMaximumCount,
LPCTSTRlpName );
参数说明:
1、第1个参数lpSemaphoreAttributes调用时填写NULL。
2、第4个参数lpName调用时填写NULL。
bool ReleaseSemaphore(HANDLE hSemaphore,
intlReleaseCount,
long*lpPreviousCount);
[if !supportLists]7.6.5 [endif] 事件(Event)
用于同一个进程内多线程间的事件的操作,暂时没有实现多进程间的事件操作。
具体实现采用linux的互斥锁(pthread_mutex_t mutex)和条件变量
(pthread_cond_t
cond)组合实现。
[if !supportLists]7.6.5.1 [endif]相关函数移植说明
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES
lpEventAttributes,
bool bManualReset,
bool bInitialState,
LPCTSTR lpName);
参数说明:
1、第1个参数lpEventAttributes调用时填写NULL。
2、第4个参数lpName调用时填写NULL。
bool SetEvent(HANDLE hEvent);
bool ResetEvent(HANDLE hEvent);
bool PulseEvent(HANDLE hEvent); 此函数无用,不需要实现。
[if !supportLists]7.6.1 [endif] 单写多读锁(SRWLOCK)
[if !supportLists]7.6.1.1.1 [endif] 数据类型定义
把linux下的读写锁简单封装为windows形式的读写锁。
typedef struct _SRWLOCK
{
pthread_rwlock_trwLock;
}SRWLOCK;
typedef SRWLOCK* PSRWLOCK;
typedef SRWLOCK* LPSRWLOCK;
[if !supportLists]7.6.1.2 [endif] 相关函数移植说明
采用linux的读写锁进行简单封装即可。
1、初始化读写锁
voidInitializeSRWLock(LPSRWLOCK pLock)
{
pthread_rwlock_init(&(pLock->rwLock),NULL);
}
2、以独占写方式使用读写锁
voidAcquireSRWLockExclusive(LPSRWLOCK pLock)
{
pthread_rwlock_wrlock(&(pLock->rwLock));
}
3、以共享读方式使用读写锁
voidAcquireSRWLockShared(LPSRWLOCK pLock)
{
pthread_rwlock_rdlock(&(pLock->rwLock));
}
4、释放之前以独占写方式使用的读写锁
voidReleaseSRWLockExclusive(LPSRWLOCK pLock)
{
pthread_rwlock_unlock(&(pLock->rwLock));
}
5、释放之前以共享读方式使用的读写锁
voidReleaseSRWLockShared(LPSRWLOCK pLock)
{
pthread_rwlock_unlock(&(pLock->rwLock));
}
6、删除读写锁,使之不可再被访问使用。
voidDeleteSRWLock(LPSRWLOCK pLock)
{
pthread_rwlock_destroy(&(pLock->rwLock));
}
注意:windows的API中没有DeleteSRWLock这个函数,为了保持代码兼容新增此函数,在linux和windows下的移植文件中都需要声明和定义此函数。
在Windows的移植文件中实现代码如下:
voidDeleteSRWLock(LPSRWLOCK pLock)
{
return;
}
如上所示,只需简单的返回即可,纯粹是为了兼容linux代码。
[if !supportLists]7.6.2 [endif] 等待同步对象(WaitForSingleObject)
WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds 为 INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。
DWORD WaitForSingleObject(HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //
等待时间
WAIT_OBJECT_0,WAIT_TIMEOUT,WAIT_FAILED,WAIT_IO_COMPLETION。
[if !supportLists]7.7 [endif] 关闭句柄(CloseHandle)
关闭句柄函数如下:
boolCloseHandle(HANDLE hObject);
函数用途:
1、用于关闭线程的句柄,线程的句柄由CreateThread函数返回。
2、关闭同步对象的句柄:互斥锁(Mutex)、事件(Event)、信号量(Semaphore)
3、其他核心对象句柄。
具体实现参考作者提供的移植文件。
[if !supportLists]7.1 [endif] 文件映射(FileMapping)
暂时没有实现,读者如果需要可自行实现。
[if !supportLists]7.2 [endif] 字符串函数
实现了对以下常用字符串相关函数的封装,这些函数是作者经常使用但是linux没有提供相应的库函数,需要在移植文件中实现封装。
int strcmpi(const char *string1, const char
*string2);
int stricmp(const char *string1, const char
*string2);
int strnicmp(const char *string1, const
char *string2, int nCount);
void strlower(char* sValue);
void strupper(char* sValue);
char* _strrev(char* sString);
char* itoa(int value, char* string, int
radix);
char* _itoa(int value, char* string, int
radix);
char* _ultoa(unsigned long value, char*
string, int radix);
char* ultoa(unsigned long value, char*
string, int radix);
int64 _atoi64(const char* string);
char* _i64toa(int64 value, char* string,
int radix);
char* _ui64toa(uint64 value, char* string,
int radix);
uint64 _atoui64(char* sValue);
uint _atoui(char* sValue);
[if !supportLists]7.3 [endif] 时间函数
[if !supportLists]7.3.1[endif] 数据类型定义
typedef struct_FILETIME
{
unsigned long dwLowDateTime;
unsigned long dwHighDateTime;
} FILETIME,*PFILETIME, *LPFILETIME;
typedef struct_SYSTEMTIME
{
unsigned short wYear;
unsigned short wMonth;
unsigned short wDayOfWeek;
unsigned short wDay;
unsigned short wHour;
unsigned short wMinute;
unsigned short wSecond;
unsigned short wMilliseconds;
} SYSTEMTIME,*PSYSTEMTIME, *LPSYSTEMTIME;
[if !supportLists]7.3.2[endif] 相关函数移植说明
BOOLSystemTimeToFileTime(LPSYSTEMTIME pSysTime, LPFILETIME pFileTime)
{
int64 nRet;
nRet = SystemTimeToInt(pSysTime);
memcpy(pFileTime, &nRet, 8);
return TRUE;
}
BOOLFileTimeToSystemTime(LPFILETIME pFileTime, LPSYSTEMTIME pSysTime)
{
int64 nRet;
memcpy(&nRet, pFileTime, 8);
IntToSystemTime(nRet, pSysTime);
return TRUE;
}
voidGetLocalTime(LPSYSTEMTIME pSystemTime)
{
struct timeval now;
gettimeofday(&now, NULL);
structtm* LocalTime;
LocalTime =localtime(&(now.tv_sec));
pSystemTime->wYear = 1900 +LocalTime->tm_year;
pSystemTime->wMonth =LocalTime->tm_mon + 1;
pSystemTime->wDay =LocalTime->tm_mday;
pSystemTime->wHour =LocalTime->tm_hour;
pSystemTime->wMinute =LocalTime->tm_min;
pSystemTime->wSecond =LocalTime->tm_sec;
pSystemTime->wMilliseconds =(now.tv_usec)/1000;
pSystemTime->wDayOfWeek =LocalTime->tm_wday;
}
voidGetSystemTime(LPSYSTEMTIME pSystemTime)
{
struct timeval now;
gettimeofday(&now, NULL);
struct tm* LocalTime;
LocalTime = gmtime(&(now.tv_sec));
pSystemTime->wYear = 1900 +LocalTime->tm_year;
pSystemTime->wMonth =LocalTime->tm_mon + 1;
pSystemTime->wDay = LocalTime->tm_mday;
pSystemTime->wHour =LocalTime->tm_hour;
pSystemTime->wMinute =LocalTime->tm_min;
pSystemTime->wSecond =LocalTime->tm_sec;
pSystemTime->wMilliseconds =(now.tv_usec)/1000;
pSystemTime->wDayOfWeek =LocalTime->tm_wday;
}
[if !supportLists]7.3.3[endif] 线程休眠函数(Sleep)
void Sleep(unsignedint dwMilliseconds)
{
timespec tt;
tt.tv_sec = dwMilliseconds/1000;
tt.tv_nsec =(dwMilliseconds%1000)*1000000;
nanosleep(&tt, NULL);
}
[if !supportLists]7.3.4[endif] 获取系统启动毫秒数(GetTickCount)
需要实现GetTickCount()、GetTickCount64()
DWORD GetTickCount();
int64GetTickCount()
{
struct timeval tv;
int64 qwVal;
gettimeofday(&tv, NULL);
qwVal = tv.tv_sec*1000 + tv.tv_usec/1000;
return qwVal;
}
[if !supportLists]7.4 [endif] socket编程
[if !supportLists]7.4.1 [endif] 相关通用宏定义
#defineINVALID_SOCKET -1
#defineSOCKET_ERROR -1
#defineSD_BOTH SHUT_RDWR
#defineSOCKET_SEND_DEFAULT_FLAG MSG_NOSIGNAL
#defineMAKEWORD(x,y) (((x<<8)&0xFF00)|(y&0xFF))
[if !supportLists]7.4.1 [endif] 相关数据类型定义
typedefint SOCKET;
typedefstruct WSAData
{
unsigned short wVersion;
unsigned short wHighVersion;
char szDescription[20];
char szSystemStatus[20];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char* lpVendorInfo;
}WSADATA;
typedefWSADATA* LPWSADATA;
[if !supportLists]7.4.2 [endif] 相关函数移植说明
[if !supportLists]7.4.2.1 [endif] WSAStartup函数
本函数必须是应用程序或DLL调用的第一个Windows Sockets函数.它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows
Sockets实现的细节.应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows
Sockets API函数.
此函数对于linux没有意义,封装此函数只需简单的返回0即可,在移植文件中此函
数声明和定义如下:
intWSAStartup(unsigned short wVersionRequested, LPWSADATA lpWSAData)
{
return 0;
}
[if !supportLists]7.4.2.2 [endif] WSACleanup函数
应用程序在完成对请求的Socket库使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
此函数对于linux没有意义,封装此函数只需简单的返回0即可,在移植文件中此函
数声明和定义如下:
intWSACleanup(void)
{
return(0);
}
[if !supportLists]7.4.2.3 [endif] closesocket函数
本函数用来关闭一个套接字。在移植文件中此函数声明和定义如下:
int closesocket(SOCKET s)
{
close(s);
return 0;
}
[if !supportLists]7.4.2.4 [endif] ioctlsocket函数
本函数用于设置套接口相关的操作参数,在移植此函数声明和定义如下:
int ioctlsocket(SOCKET s, unsigned long dwCmd,unsigned long* pArg)
{
return(ioctl(s, nCmd, pArg));
}
[if !supportLists]7.4.2.5 [endif] 其它函数使用说明
以下函数不需要移植,windows和 linux的函数定义和语义是相同或类似。
1、socket()
生成一个套接字。
2、bind()
绑定套接字的本地IP地址和端口号。
3、listen()
用于TCP监听套接字。
4、accept()
接收来自外部的TCP连接请求。
5、connect()
连接远程TCP服务器。
6、recv()
用于TCP连接接收数据。
7、send()
8、recvfrom()
用于UDP连接接收数据。
9、sendto()
用于UDP连接发送数据。
10、select()相关函数,实现多路IO复用。
FD_ZERO、FD_SET、FD_ISSET、FD_CLR
[if !supportLists]7.4.3[endif] 关于大并发TCP编程
Linux或windows平台采用select函数的多路复用方案, 最大支持上千个并发TCP连接访问。如果想支持几万、几十万乃至上百万的TCP并发连接,在linux下需要采用epoll网络编程,但是在windows/VC平台上不支持epoll的开发接口,只是提供完成端口方式。作者通过使用VC的select函数封装成epoll的函数形式,这样就可以在VC上使用epoll函数进行开发、调试、测试,由于linux本来就支持 epoll,移植过去毫无障碍。关于在VC下用select相关函数封装成epoll函数作者在专门的开发手册中会详细介绍。
作者体会: Windows对支持大TCP并发很糟糕,即使使用完成端口。 强烈建议使用linux/epoll的方案实现支持TCP大规模访问。
[if !supportLists]7.5 [endif] 读取配置文件函数
windows提供以下2个API函数读取Windows的ini配置文件。
UINT GetPrivateProfileInt(const char*
lpAppName,
constchar* lpKeyName,
int nDefault,
constchar* lpFileName);
unsigned int GetPrivateProfileString(const
char* lpAppName,
const char* lpKeyName,
const char* lpDefault,
char* lpReturnedString,
unsigned int nSize,
const char* lpFileName);
暂时作者没有提供修改配置文件的函数WritePrivateProfileString()。
[if !supportLists]7.6 [endif] 动态库相关函数
1、加载动态库函数
HINSTANCELoadLibrary(char* sFileName)
{
return(dlopen(sFileName, RTLD_LAZY));
}
2、获取动态库函数的地址
void*GetProcAddress(HINSTANCE hInst, char* sFunctionName)
{
return(dlsym(hInst, sFunctionName));
}
3、卸载动态库函数
BOOLFreeLibrary(HINSTANCE hInst)
{
int nRet;
nRet = dlclose(hInst);
if(nRet == 0)
{
return TRUE;
}
else
{
return FALSE;
}
}
[if !supportLists]7.7 [endif] 文件/目录兼容
[if !supportLists]7.7.1 [endif] 宏定义
由于windows目录采用符号:\ 分隔,而linuxmu.lu采用符号/分隔,源码中的目录必须采用以下的宏定义。
#define PATH_SP '/'
#define PATH_SPS "/"
实例代码:
charsRootDir[1024];
sprintf(sRootDir,"%s%cmain.ini", GetCurrentDir(), PATH_SP);
假设函数GetCurrentDir()返回当前路径。
[if !supportLists]7.7.2 [endif] 相关函数
作者提供了以下目录和文件操作的函数:
1、获取当前路径
intGetCurrentDirectory(int len, char* sBuf);
2、删除指定文件
BOOLDeleteFile(char* sFile);
3、创建目录
BOOLCreateDirectory(char* sFilePath, void* pPara);
4、移动文件
BOOLMoveFile(char* sSourFile, char* sDestFile);
5、文件重命名
rename(char*sSourFile, char* sDestFile);
[if !supportLists]7.8 [endif] 系统错误码
常用错误码的宏定义如下:
#define WSAEWOULDBLOCK EWOULDBLOCK
#define PEINPROGRESS EINPROGRESS
#define ERROR_ALREADY_EXISTS EEXIST
以下函数返回线程当前的错误码,函数声明和定义如下:
intWSAGetLastError()
{
return errno;
}
intGetLastError()
{
return errno;
}
[if !supportLists]8 [endif] VC工程源码兼容性调整
[if !supportLists]8.1 [endif] 建议
1、不试用任何MFC类库,推荐试用标准库跨平台的第三方功能类库。
2、文件操作尽可能试用FILE(fopen、fclose、fseek、fread、fwrite)以及标准的IO函数(open、
close、read、write等)
3、c/c++语法选取VC和GCC/G++共同支持的语法规则
4、socket编程不要实用VC特有支持异步事件的函数WSA,实用标准的socket函数。
[if !supportLists]8.2 [endif] 准备移植文件
作者提供了移植文件为:
PortWindows.cpp、PortWindows.h,开发人员把这2个文件加入到VC的工程中进行编译。
[if !supportLists]8.3 [endif] 宏定义
在移植文件中进行以下宏定义
#define MSG_NOSIGNAL 0
[if !supportLists]8.1 [endif] socket函数调整
1、send()函数
用于TCP连接发送数据,注意第4个参数在VC下默认填写0,建议在使用
MSG_NOSIGNAL宏定义。
示例代码如下:
int nRet;
nRet = send(s, "1234567890", 10, MSG_NOSIGNAL);
[if !supportLists]8.2 [endif] 新增释放读写锁
Windows的读写锁不需要释放,也没有对应的函数,为了兼容linux,新增一个释放读写锁的函数DeleteSRWLock,此函数不执行任何功能,直接返回。
voidDeleteSRWLock(LPSRWLOCK pLock)
{
return;
}
[if !supportLists]9 [endif] 本方案的功能限制
由于作者一向倾向于采用单进程多线程的方式实现服务器程序,本方案的实现对支持多进程的编程开始模式很弱。例如对进程间通讯的支持基本没实现,例如:共享内存、信号量、消息队列、管道等,还有就是进程间的线程锁互斥和同步、文件映射等。如果读者需要,可根据需要自行实现或采用其他的方案解决。
[if !supportLists]10[endif] 本方案的缺点
1、基于本文的移植方案的linux程序相比原生的linux程序,运行效率会降低一点,但是相
对整体来说,可以忽略不记,毕竟只是增加了一层简单的移植外壳代码。
2、有些Windows下的代码无法通过linux的移植文件实现,建议windows和linux分别实现,
然后通过加入预编译指令,分别编译。
3、需要购买VC的正版授权。
4、需要面临被微软断供的可能。
[if !supportLists]11[endif] 成功案例
作者长期从事C/C++进行后台服务器程序开发,目前所有的项目或工程都支持跨平台,项目包括:
1、功能类库SDK
1)内存分配管理
2)数据结构类
链表、avl二叉树、红黑二叉树、trie查找树、trie模糊树、多级目录模糊匹配树、多
级树、内存表。
3)解析类库
xml解析、Json解析等。
4)其他
消息队列等。
2、内存数据库系统
3、脚本引擎
4、WebServer产品
5、消息网关类
短信网关、MQTT服务器系统等消息转发处理产品。
以上所有的项目都是基于VC开发、调试测试,然后一次性移植到linux平台,效果明显。