基于VC开发linux服务器 程序指南(V1.0)

 

 

基于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平台,效果明显。

你可能感兴趣的:(基于VC开发linux服务器 程序指南(V1.0))