基于VC++开发linux服务器程序指南

 

                                 基于VC++开发linux服务器程序指南

 

 

 

  1.  概述

在此仅把此文献给那些奋斗在一线的c/c++开发人员,希望可以帮到大家。这是作者多年跨平台编程经验的总结,以文档的形式供大家参考。

 

  1.  背景

本人一直基于微软Microsoft Visual C++(以下简称VC)开发后台服务器程序,

由于项目需要,程序需要运行在linux/unix(以下的linux代表Linux和unix)

平台。但是本人已经非常熟悉和依赖VC,不打算再掌握linux下的开发工具进

行开发。 如果采用linux特有的API函数,windows源程序需要进行大量的修

改。在朋友的指点下,采用基于VC开发、调试、测试,然后移植到linux/g++

进行编译运行。随着移植文件的完善和编码兼容性的优化,现在基本上可以做到VC的工程迁移到linux,只需要做很少或不做修改即可编译通过。

 

  1.  采用本方案目的

采用本方案希望达成以下几点目的。

1、充分利用微软公司VC强大的可视化编辑、调试功能。同时可使用配套的版本管理工具

   和Visual Assit X开发助手,有助于提高开发过程的自动化和开发效率

2、实现开发服务器程序跨平台(windows、linux、unix等)、跨开发工具(VC、g++等)。

3、只需在VC下一次编写源码、一次调试、一次测试,然后到其他平台只需要编译、运行

   即可。

4、加快代码的编写速度和质量。

5、加快代码调试速度和质量。

6、加快程序乃至整个项目的上线进度。

 

本方案主要是移植大部分常用的功能函数,无意移植所有的功能函数。首先是有些函数由于操作系统的差异无法移植,或者移植代价太大完全没有必要。

 

给开发人员提供多一种跨平台项目开发的选择,无意也不可能取代linux/g++开发工具和其他跨平台开发解决方案。

 

本方案最适合开发后台服务器程序,如果开发人员要应用于其他领域,需要慎重考虑可行性。

 

  1.  适用领域

本文适合以下读者:

 

1、本文适用读者是面向VC开发人员,他们需要进行linux下进行开发服务器程

序。 采用本方案对VC开发人员技能要求:熟悉VC开发工具、熟悉C/C/++、熟

悉标准库(可选)、熟悉windows核心编程(多线程、线程同步等)、Socket编

程、了解linux和g++基本知识,不要求掌握MFC。

 

2、现有的windows平台下的VC工程需要移植到linux平台下运行。

 

3、针对linux/g++ 开发人员也具有参考意义。

 

采用本方案的领域:后台服务器程序。

  1.  总体思路

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

 

  1.  开发工具和操作系统要求
  2.  Windows/VC

 

1、建议采用/VC 2008及以上版本,同时安装Visual Assist X开发助手。

2、操作系统

   1)桌面系统采用64位Win7及以上版本。

   2)服务器系统采用Windows 2008x64及以上版本

3、其他

    如果只是开发32位程序,建议安装VC6,同时安装Visual Assist X开发助

    手。

 

  1.  linux/g++

作者开发选用RedHat和CentOS 的linux,如果开发人员希望在其他厂商的linux上开发,需要进行进一步的测试

 

  1.  unix/g++

作者没有在unix下开发的经历,如果开发人员在unix(HP_UX、AIX、Solaris、BSD)下采用本方案,需要进行进一步测试作者提供的移植文件,如果有必要可进行修改和完善。

 

  1.  linux移植工作

 

需要在linux/g++下编写移植文件,作者提供2个移植文件PortLinux.cpp和

PortLinux.h,开发人员需要在自己的源码文件(c、cpp、h)中包含(#include)PortLinux.h文件。

 

编写移植文件需要熟悉linux/gcc/g++的开发人员协助VC开发人员一起编写,移植文件需要在linux 下编译、调试、测试,后续就可以把移植文件加入到g++工程里进行整体编译。移植工作只需要做一次即可,但是做到可以一劳永逸。

 

  1.  常用宏定义

 

#define INFINITE    0xFFFFFFFF

#define WINAPI

 

  1.  布尔类型常量宏定义

 

#define FALSE  0

#define TRUE  1

 

 

  1.  数据类型定义

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

{

       long long int QuadPart;

}LARGE_INTEGER;

 

  1.  初始化函数

初始化全局变量:获取操作系统开机时间

 

InitSysPort();

 

  1.  线程相关移植
  2.  类型定义

1、在移植文件中进行数据类型定义LPSECURITY_ATTRIBUTES;

    typedef char* LPSECURITY_ATTRIBUTES;

    用途:用于封装CreateThread函数。

 

2、LPTHREAD_START_ROUTINE

 

       extern "C"

       {

              typedef void* (*LPTHREAD_START_ROUTINE)(void *);

       }

 

   用途:用于封装CreateThread函数。

 

      1.  相关函数

 

        1.  CreateThread函数

 

功能:CreateThread是一种微软在Windows API中提供了建立一个新的线程的

    函数,该函数在主线程的基础上创建一个新线程。线程终止运行后,线程对

    象仍然在系统中,必须通过CloseHandle函数来关闭该线程对象。

 

   函数定义如下:

        HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,

                                   unsigned int dwStackSize,

                                   LPTHREAD_START_ROUTINE lpStartAddress,

                                   LPVOID lpParameter,

                                   unsigned int dwCreationFlags,

                                   unsigned long* lpThreadId);

 

   函数使用说明:

   1、其中第1、5参数在linux/g++没有意义,填写默认参数。

     1)第1个参数lpThreadAttributes,赋值为NULL。

     2)第5个参数dwCreationFlags,赋值为0。

 

   2、执行成功返回非NULL的句柄。

 

        1.  TerminateThread函数

 

   功能:TerminateThread线程外终止一个线程,用于强制终止线程。

 

  函数声明如下:

   BOOLTerminateThread( HANDLE hThread, DWORDdwExitCode);

 

bool SetThreadPriority(HANDLE hThread, int nPriority);

 

        1.  GetCurrentThreadId函数

 

获取当前线程一个唯一的线程标识符。

 

int GetCurrentThreadId ();

 

        1.  SetThreadPriority函数

暂时没有实现。

 

 

        1.  其他说明

1、不要封装暂停线程(SuspendThread)和恢复线程(ResumeThread)函数,主要是没有必要使

   用这2个函数,另外在linux下也没有类似的函数和功能。

 

  1.  同步对象

 

  1.  通用宏定义

 

#define WAIT_OBJECT_0              00

#define WAIT_TIMEOUT               01

 

 

  1.  临界区(CriticalSection)

用于同一个进程内多线程间的互斥。

        1.  数据类型定义

typedef pthread_mutex_t CRITICAL_SECTION;

typedef CRITICAL_SECTION* LPCRITICAL_SECTION;

 

        1.  相关函数移植说明

采用linux的互斥锁进行简单封装即可。

 

void InitializeCriticalSection(CRITICAL_SECTION* lpCriticalSection)

{

         pthread_mutex_init(lpCriticalSection, NULL);

}

 

void EnterCriticalSection(CRITICAL_SECTION* lpCriticalSection)

{

         pthread_mutex_lock(lpCriticalSection);

}

 

BOOL TryEnterCriticalSection(CRITICAL_SECTION* lpCriticalSection)

{

         int nret;

         nret = pthread_mutex_trylock(lpCriticalSection);

         if(nret == 0)

         {

                   return TRUE;

         }

         else

         {

                   return FALSE;

         }

}

 

void LeaveCriticalSection(CRITICAL_SECTION* lpCriticalSection)

{

         pthread_mutex_unlock(lpCriticalSection);

}

 

void DeleteCriticalSection(CRITICAL_SECTION* lpCriticalSection)

{

       pthread_mutex_destroy(lpCriticalSection);

}

 

  1.  互斥锁(mutex)

用于同一个进程内多线程间的互斥锁的操作,暂时没有实现多进程间的互斥锁操作。

 

HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);

 

1、第1个参数lpMutexAttributes调用时填写NULL。

2、第4个参数lpName调用时填写NULL。

 

bool ReleaseMutex(HANDLE hMutex);

 

  1.  信号量(Semaphore)

用于同一个进程内多线程间的信号量的操作,暂时没有实现多进程间的信号量操作。

 

具体实现采用linux的互斥锁(pthread_mutex_t mutex)和条件变量

(pthread_cond_t cond)组合实现。

   

HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

                     int lInitialCount,

                     int lMaximumCount,

                     LPCTSTR lpName );

 

参数说明:

1、第1个参数lpSemaphoreAttributes调用时填写NULL。

2、第4个参数lpName调用时填写NULL。

 

bool ReleaseSemaphore(HANDLE hSemaphore,

                     int lReleaseCount,

                     long* lpPreviousCount);

 

  1.  事件(Event)

用于同一个进程内多线程间的事件的操作,暂时没有实现多进程间的事件操作。

 

具体实现采用linux的互斥锁(pthread_mutex_t mutex)和条件变量

(pthread_cond_t cond)组合实现。

 

        1. 相关函数移植说明

 

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); 此函数无用,不需要实现。

 

      1.  单写多读锁(SRWLOCK)

 

          1.  数据类型定义

把linux下的读写锁简单封装为windows形式的读写锁。

typedef struct _SRWLOCK

{

       pthread_rwlock_t rwLock;

}SRWLOCK;

typedef SRWLOCK* PSRWLOCK;

typedef SRWLOCK* LPSRWLOCK;

 

        1.  相关函数移植说明

采用linux的读写锁进行简单封装即可。

 

1、初始化读写锁

void InitializeSRWLock(LPSRWLOCK pLock)

{

         pthread_rwlock_init(&(pLock->rwLock), NULL);

}

 

2、以独占写方式使用读写锁

void AcquireSRWLockExclusive(LPSRWLOCK pLock)

{

         pthread_rwlock_wrlock(&(pLock->rwLock));

}

 

3、以共享读方式使用读写锁

void AcquireSRWLockShared(LPSRWLOCK pLock)

{

         pthread_rwlock_rdlock(&(pLock->rwLock));

}

 

4、释放之前以独占写方式使用的读写锁

void ReleaseSRWLockExclusive(LPSRWLOCK pLock)

{

         pthread_rwlock_unlock(&(pLock->rwLock));

}

 

5、释放之前以共享读方式使用的读写锁

void ReleaseSRWLockShared(LPSRWLOCK pLock)

{

         pthread_rwlock_unlock(&(pLock->rwLock));

}

 

6、删除读写锁,使之不可再被访问使用。

void DeleteSRWLock(LPSRWLOCK pLock)

{

         pthread_rwlock_destroy(&(pLock->rwLock));

}

 

注意:windows的API中没有DeleteSRWLock这个函数,为了保持代码兼容新增此函数,在linux和windows下的移植文件中都需要声明和定义此函数。

在Windows的移植文件中实现代码如下:

 

void DeleteSRWLock(LPSRWLOCK pLock)

{

         return;

}

如上所示,只需简单的返回即可,纯粹是为了兼容linux代码。

 

      1.  等待同步对象(WaitForSingleObject)

 

WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds  INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。

 

DWORD WaitForSingleObject(HANDLE hObject, //指明一个内核对象的句柄
DWORD dwMilliseconds); //等待时间

 

WAIT_OBJECT_0,WAIT_TIMEOUT,WAIT_FAILED,WAIT_IO_COMPLETION。

  1.  关闭句柄(CloseHandle)

关闭句柄函数如下:

   bool CloseHandle(HANDLE hObject);

 

函数用途:

1、用于关闭线程的句柄,线程的句柄由CreateThread函数返回。

2、关闭同步对象的句柄:互斥锁(Mutex)、事件(Event)、信号量(Semaphore)

3、其他核心对象句柄。

 

具体实现参考作者提供的移植文件。

 

    1.  文件映射(FileMapping)

暂时没有实现,读者如果需要可自行实现。

 

  1.  字符串函数

实现了对以下常用字符串相关函数的封装,这些函数是作者经常使用但是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);

 

  1.  时间函数

 

      1.  数据类型定义

 

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;

 

      1.  相关函数移植说明

BOOL SystemTimeToFileTime(LPSYSTEMTIME pSysTime, LPFILETIME pFileTime)

{

         int64 nRet;

         nRet = SystemTimeToInt(pSysTime);

         memcpy(pFileTime, &nRet, 8);

         return TRUE;

}

 

BOOL FileTimeToSystemTime(LPFILETIME pFileTime, LPSYSTEMTIME pSysTime)

{

         int64 nRet;

         memcpy(&nRet, pFileTime, 8);

         IntToSystemTime(nRet, pSysTime);       

         return TRUE;

}

 

void GetLocalTime(LPSYSTEMTIME pSystemTime)

{

         struct timeval now;

         gettimeofday(&now, NULL);

 

     struct tm* 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;    

}

 

void GetSystemTime(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;    

}

 

      1.  线程休眠函数(Sleep)

 

void Sleep(unsigned int dwMilliseconds)

{

         timespec tt;

         tt.tv_sec = dwMilliseconds/1000;

         tt.tv_nsec = (dwMilliseconds%1000)*1000000;

         nanosleep(&tt, NULL);

}

 

      1.  获取系统启动毫秒数(GetTickCount)

 

需要实现GetTickCount()、GetTickCount64()

 

DWORD GetTickCount();

 

 

 

int64 GetTickCount()

{

       struct timeval tv;

       int64 qwVal;

       gettimeofday(&tv, NULL);

       qwVal = tv.tv_sec*1000 + tv.tv_usec/1000;

       return qwVal;

}

 

  1.  socket编程

 

  1.  相关通用宏定义

 

       #define INVALID_SOCKET               -1

       #define SOCKET_ERROR              -1

       #define SD_BOTH                                   SHUT_RDWR

       #define SOCKET_SEND_DEFAULT_FLAG MSG_NOSIGNAL                                 

       #define MAKEWORD(x,y)             (((x<<8)&0xFF00)|(y&0xFF))

 

      1.  相关数据类型定义

 

       typedef int SOCKET;

 

       typedef struct WSAData

       {

              unsigned short wVersion;

           unsigned short wHighVersion;

           char szDescription[20];

           char szSystemStatus[20];

           unsigned short iMaxSockets;

           unsigned short iMaxUdpDg;

           char* lpVendorInfo;

       }WSADATA;

       typedef WSADATA* LPWSADATA;

 

      1.  相关函数移植说明

 

        1.  WSAStartup函数

本函数必须是应用程序或DLL调用的第一个Windows Sockets函数.它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节.应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数.

 

       此函数对于linux没有意义,封装此函数只需简单的返回0即可,在移植文件中此函

       数声明和定义如下:

   

    int WSAStartup(unsigned short wVersionRequested, LPWSADATA lpWSAData)

    {

              return 0;

    }

 

        1.  WSACleanup函数

应用程序在完成对请求的Socket库使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。

 

       此函数对于linux没有意义,封装此函数只需简单的返回0即可,在移植文件中此函

       数声明和定义如下:

 

       int WSACleanup(void)

       {

                     return(0);

       }

 

        1.  closesocket函数

本函数用来关闭一个套接字。在移植文件中此函数声明和定义如下:

 

int closesocket(SOCKET s)

{

        close(s);

        return 0;

}

 

        1.  ioctlsocket函数

本函数用于设置套接口相关的操作参数,在移植此函数声明和定义如下:

 

int ioctlsocket(SOCKET s, unsigned long dwCmd, unsigned long* pArg)

{

       return(ioctl(s, nCmd, pArg));

}

 

        1.  其它函数使用说明

以下函数不需要移植,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

 

      1.  关于大并发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大规模访问。

 

  1.  读取配置文件函数

windows提供以下2个API函数读取Windows的ini配置文件。

 

UINT GetPrivateProfileInt(const char* lpAppName,

                                          const char* lpKeyName,

                                           int nDefault,

                                          const char* lpFileName);

 

unsigned int GetPrivateProfileString(const char* lpAppName,

                                                   const char* lpKeyName,

                                                   const char* lpDefault,

                                                   char* lpReturnedString,

                                                   unsigned int nSize,

                                                   const char* lpFileName);

 

 

暂时作者没有提供修改配置文件的函数WritePrivateProfileString()。

 

  1.  动态库相关函数

1、加载动态库函数

HINSTANCE LoadLibrary(char* sFileName)

{

         return(dlopen(sFileName, RTLD_LAZY));

}

 

2、获取动态库函数的地址

void* GetProcAddress(HINSTANCE hInst, char* sFunctionName)

{

         return(dlsym(hInst, sFunctionName));

}

 

3、卸载动态库函数

BOOL FreeLibrary(HINSTANCE hInst)

{

         int nRet;

         nRet = dlclose(hInst);

         if(nRet == 0)

         {

                   return TRUE;

         }

         else

         {

                   return FALSE;

         }

}

 

  1.  文件/目录兼容

 

  1.  宏定义

由于windows目录采用符号:\ 分隔,而linuxmu.lu采用符号/分隔,源码中的目录必须采用以下的宏定义。

 

#define PATH_SP     '/'

#define PATH_SPS   "/"

 

实例代码:

       char sRootDir[1024];

       sprintf(sRootDir, "%s%cmain.ini", GetCurrentDir(), PATH_SP);

 

假设函数GetCurrentDir()返回当前路径。

 

  1.  相关函数

作者提供了以下目录和文件操作的函数:

 

1、获取当前路径

       int GetCurrentDirectory(int len, char* sBuf);

 

2、 删除指定文件

   BOOL DeleteFile(char* sFile);

 

3、创建目录

   BOOL CreateDirectory(char* sFilePath, void* pPara);

 

4、移动文件

    BOOL MoveFile(char* sSourFile, char* sDestFile);

 

5、 文件重命名

   rename(char* sSourFile, char* sDestFile);

 

  1.  系统错误码

常用错误码的宏定义如下:

#define WSAEWOULDBLOCK                 EWOULDBLOCK

#define PEINPROGRESS                          EINPROGRESS

#define ERROR_ALREADY_EXISTS        EEXIST

 

以下函数返回线程当前的错误码,函数声明和定义如下:

 

       int WSAGetLastError()

       {

              return errno;

       }

 

       int GetLastError()

       {

              return errno;

       }

 

  1.  VC工程源码兼容性调整

 

  1.  建议

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函数。

  1.  准备移植文件

作者提供了移植文件为: PortWindows.cpp、PortWindows.h,开发人员把这2个文件加入到VC的工程中进行编译。

  1.  宏定义

在移植文件中进行以下宏定义

 

#define MSG_NOSIGNAL 0

 

    1.  socket函数调整

1、send()函数

  用于TCP连接发送数据,注意第4个参数在VC下默认填写0,建议在使用

  MSG_NOSIGNAL宏定义。

 

  示例代码如下:

   int nRet;

   nRet = send(s, "1234567890", 10, MSG_NOSIGNAL);

 

    1.  新增释放读写锁

Windows的读写锁不需要释放,也没有对应的函数,为了兼容linux,新增一个释放读写锁的函数DeleteSRWLock,此函数不执行任何功能,直接返回。

 

void DeleteSRWLock(LPSRWLOCK pLock)

{

         return;

}

 

  1.  本方案的功能限制

由于作者一向倾向于采用单进程多线程的方式实现服务器程序,本方案的实现对支持多进程的编程开始模式很弱。例如对进程间通讯的支持基本没实现,例如:共享内存、信号量、消息队列、管道等,还有就是进程间的线程锁互斥和同步、文件映射等。如果读者需要,可根据需要自行实现或采用其他的方案解决。

 

  1.  本方案的缺点

1、基于本文的移植方案的linux程序相比原生的linux程序,运行效率会降低一点,但是相

   对整体来说,可以忽略不记,毕竟只是增加了一层简单的移植外壳代码。

 

2、有些Windows下的代码无法通过linux的移植文件实现,建议windows和linux分别实现,  

    然后通过加入预编译指令,分别编译。

 

3、需要购买VC的正版授权。

 

4、需要面临被微软断供的可能。

  1.  成功案例

作者长期从事C/C++进行后台服务器程序开发,目前所有的项目或工程都支持跨平台,项目包括:

 

1、功能类库SDK

   1)内存分配管理

 

   2)数据结构类

      链表、avl二叉树、红黑二叉树、trie查找树、trie模糊树、多级目录模糊匹配树、多

      级树、内存表。

 

   3)解析类库

      xml解析、Json解析等。

 

  4)其他

    消息队列等。

 

2、内存数据库系统

 

3、脚本引擎

 

4、WebServer产品

 

5、消息网关类

   短信网关、MQTT服务器系统等消息转发处理产品。

 

以上所有的项目都是基于VC开发、调试测试,然后一次性移植到linux平台,效果明显。

 

 

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

编程农场

我很欣赏你的眼光和智慧。

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值