# PNetProcess **Repository Path**: chengp2020/pnet-process ## Basic Information - **Project Name**: PNetProcess - **Description**: 轻量级、简单易用的,在Windows系统中实现TCP连接和数据传输功能的代码。 编译环境Visual Studio 2019,使用MFC实现测试界面。 - **Primary Language**: C++ - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 3 - **Created**: 2020-10-10 - **Last Updated**: 2022-09-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 1 简介 NetProcess是一个在Windows系统中实现TCP连接和数据传输功能的代码模块。 轻便易用,只需4个文件即可在你的代码中整合网络传输功能。 提供性能和可扩展性的保障,模块使用线程池机制实现,同时处理多至数千个连接并收发数据,可同时建立多个网络服务和客户端连接。 适合VC++32位及64位编译环境,测试程序使用Visual Studio 2019及MFC实现。 以下简要说明代码的使用方式,具体实现见测试代码。 ## 2 文件说明 在你的工程中包含以下4个代码文件即可整合网络传输功能: NetProcess.h -- 网络传输功能代码头文件; NetProcess.cpp -- 网络传输功能代码实现文件; PQueue.h -- 一个队列模板类; PSyncQueue.h -- 一个线程安全的队列模板类。 其他文件为测试程序使用。 ## 3 代码说明 ### 3.1 class PNetLink 指示一个线程中的socket连接,并定义数据连接和发送的功能。其中id指定本地标识,用于使用者标明一个连接。 ### 3.2 class PLinkStat 定义连接状态的类,包括IP地址和收发数据量等信息。 ### 3.3 class PNetItem 包括连接、状态、数据及数据长度的类,使用智能指针实现,可自动销毁资源,便于在线程之间传送状态和数据。 ### 3.4 传输模式 代码中定义以下几种数据传输模式,跟据不同模式收发数据包。模式由listen和connect函数的mode参数指定,要求服务端和客户端一致。 ```C++ #define _NET_MODE_MESSAGE 1 //字节消息传输,要求指定结束符 #define _NET_MODE_WIDE_MESSAGE 2 //宽字节消息传输,要求指定结束符 #define _NET_MODE_PACKET 3 //整包传输,包头指定数据长度 #define _NET_MODE_FIXED 4 //接收定长数据,不改变发送数据 #define _NET_MODE_CELL 5 //传输(发送和接收)定长数据,发送数据按定长分组 #define _NET_MODE_DATA 6 //传输无格式数据 ``` _NET_MODE_MESSAGE:以结束符分隔的数据传输模式,可用于文本消息的传输。listen和connect函数的letter参数指定分隔符。发送数据时在包的结尾自动添加分隔符,接收数据时检查并去掉分隔符,保证用户收到为完整的数据包(不包括分隔符)。 _NET_MODE_WIDE_MESSAGE:用于宽字节(wchar_t)文本消息的传输,其它与_NET_MODE_MESSAGE模式相同。 _NET_MODE_PACKET:包数据传输模式,可用于二进制数据的传输。发送数据时在用户数据前添加数据长度信息,接收数据时首先接收长度信息,再接收此长度的数据完成后提供给用户,保证用户收到为完整的数据包(不包括附加的数据长度)。 _NET_MODE_FIXED:定长数据包传输模式,发送原始数据,不添加任何信息。listen和connect函数的letter参数指定数据长度,接收到letter长度的数据后提供给用户,保证用户收到该长度的数据包。 _NET_MODE_CELL:信元传输模式,程序将数据分割为指定长度的信元并发送。listen和connect函数的letter参数指定信元数据长度,接收到letter长度的数据后提供给用户。此模式可能用在快速数据转发的场景中;此模式不会将数据组合为完整的数据包,因此需要使用者自定义信元数据的处理方式(例如:使用第一个信元指定接收者、数据类型、长度、信元数等信息)。 _NET_MODE_DATA:无格式数据传输模式,发送和接收原始数据,不添加任何信息。收到的数据可能因缓冲的原因而被分割在不同的包中。 ## 4 使用方法 ### 4.1 启动 初始化网络环境,启动线程。调用startNetProcess函数,指定回调函数。 ```C++ //启动网络连接线程,参数为指定的数据接收回调函数 bool startNetProcess(PNotifyFunction fun); ``` 程序使用回调机制实现与使用者的通讯,fun 为接收数据和状态消息的回调函数指针;使用者需定义这个函数以接收网络状态和数据通知,回调函数类型的定义如下: ```C++ typedef void (*PNotifyFunction)(PNetItem item);//接收网络数据和连接状态的回调函数类型 ``` ### 4.2 结束 调用exitNetProcess函数,退出网络线程,断开所有当前的连接及销毁关联的资源。 ```C++ //退出网络线程,断开所有当前的连接 void exitNetProcess(); ``` ### 4.3 服务端 建立一个监听连接(同时指定连接模式及参数),调用静态函数listen,代码形式如下: ```C++ PNetLink::listen("0.0.0.0", 9999); ``` 或者 ```C++ PNetLink::listen("0.0.0.0", 9999, 0, _NET_MODE_MESSAGE, 127); ``` listen函数参数说明: ip,port:ip地址和端口; _id:本地标识,由使用者指定此连接的编号; _limit:数据包的最大长度。 ```C++ //网络服务端,建立网络监听 _id:监听socket的本地标识,limit数据包的最大长度 static void listen(const char* ip, int port, int _id = 0, int _mode = _NET_MODE_PACKET, int _letter = 0, int _limit = _NET_PACKET_LIMIT); static void listen(const wchar_t* ip, int port, int _id = 0, int _mode = _NET_MODE_PACKET, int _letter = 0, int _limit = _NET_PACKET_LIMIT); ``` 接受连接: 程序通过回调函数通知用户,item的code()值为_NET_CODE_ACCEPT时,即为接受到新的连接。 以下代码作为说明,其中item为传入的参数,link为用户定义的连接。 ```C++ PNetItem item;//收到的连接参数 if (item.code() == _NET_CODE_ACCEPT) { PNetLink link = item;//将收到的连接赋值给本地连接 } ``` 接受的连接会继承监听连接的属性(包括模式、letter及数据包最大长度)。 ### 4.4 客户端 建立一个连接,调用静态函数connect,代码形式如下: ```C++ PNetLink::connect("127.0.0.1", 9999, 1); ``` 或者 ```C++ PNetLink::connect("127.0.0.1", 9999, 1, _NET_MODE_MESSAGE, 127); ``` connect函数参数说明: ip,port:ip地址和端口; _id:本地标识,由使用者指定此连接的编号; _limit:数据包的最大长度。 ```C++ //客户端建立网络连接,指定ip地址和端口,_id本地标识,_limit数据包的最大长度 static void connect(const char* ip, int port, int _id = 0, int _mode = _NET_MODE_PACKET, int _letter = 0, int _limit = _NET_PACKET_LIMIT); static void connect(const wchar_t* ip, int port, int _id = 0, int _mode = _NET_MODE_PACKET, int _letter = 0, int _limit = _NET_PACKET_LIMIT); ``` 连接成功通知: 调用connect函数之后并不会立即得到网络连接,程序会调用回调函数通知用户,当item的code()值为_NET_CODE_CONNECT时,表示连接成功。 item为新连接,id()属性确定此连接的身份,因此必要时使用者需在调用connect函数时指定id。 code()值为_NET_FAIL_CONNECT时,表示网络连接失败。 如何接受成功的连接,以下代码作为说明,其中item为传入的参数,link为用户定义的连接。 ```C++ PNetItem item;//收到的连接参数 if (item.code() == _NET_CODE_CONNECT) { PNetLink link = item;//将收到的连接赋值给本地连接 } ``` ### 4.5 发送数据 调用PNetLink类的send函数发送数据。 ```C++ //以下发送函数发送数据包,数据传输格式由连接的mode确定,函数将多组数据合并发送 //CELL模式将每组数据按cell发出,不会跨越组 void send(const char* p); void send(const wchar_t* p); void send(const char* p, int len); void send(const char* p1, int len1, const char* p2, int len2); void send(const char* p1, int len1, const char* p2, int len2, const char* p3, int len3); ``` ### 4.6 接收数据 程序调用回调函数通知用户,当item的code()值为_NET_CODE_DATA时,表示接收到数据。 _NET_MODE_MESSAGE、_NET_MODE_WIDE_MESSAGE及_NET_MODE_PACKET传输模式,此时收到的为完整数据包(不包括分隔符、数据长度等附加信息); _NET_MODE_FIXED、_NET_MODE_CELL传输模式,此时收到的为定长(长度为letter)数据包。 对于声明形式如下的回调函数 ```C++ void recvFunction(PNetItem item); ``` 参数item为收到数据的连接,item的data()和length()属性为数据指针和长度。 程序在发送和接收数据失败时会断开此连接,并向用户通知错误信息。 ### 4.7 网络状态 程序调用回调函数向用户通知网络状态。 对于声明形式如下的回调函数 ```C++ void recvFunction(PNetItem item); ``` 参数item为状态的连接,item的code()为网络状态码;当item的code()为_NET_CODE_STAT时,state()包含连接状态的信息(ip地址、端口、收发数据量等)。 以下为网络状态编码定义: ```C++ #define _NET_CODE_DATA 0 #define _NET_CODE_LISTEN 1 #define _NET_CODE_CONNECT 2 #define _NET_CODE_ACCEPT 3 #define _NET_CODE_DISCONNECT 4 #define _NET_CODE_STAT 5 #define _NET_CODE_EXIT -1 #define _NET_FAIL_THREAD -2 #define _NET_FAIL_LISTEN -3 #define _NET_FAIL_CONNECT -4 #define _NET_FAIL_ACCEPT -5 #define _NET_REJECT_CONNECT -6 #define _NET_LOST_CONNECT -7 #define _NET_FAIL_SEND -8 #define _NET_FAIL_RECV -9 #define _NET_ERROR_EVENT -10 ``` code > 0 时表示一个状态正常的网络通知消息。 code == 0 时表示收到数据的通知消息。 code < 0 时表示网络连接发生错误,这时此连接已经断开。 调用getNetCodeDescribe函数,可获得状态代码的文本描述。 ```C++ //获得消息编码的文本说明 const char * getNetCodeDescribe(int code); ```