VC2005 Socket Server封装库

1、说明

     早期为第三方系统对接编写的C++ DLL封装库,提供Socket Server服务,支持多个客户端连接,由服务端单向发送数据,可以支持多款操作系统和实现多种语言调用支持。

2、API定义

  DLL主要通过FishFlyCommServer.def文件暴露接口,形成标准WinAPI。

; FishFlyCommServer.def : 声明 DLL 的模块参数。

LIBRARY      "FishFlyCommServer"

EXPORTS
    FishFlyCommServerStart
    FishFlyCommServerStop
    FishFlyCommServerRead
    FishFlyCommServerWrite

接收部分仅支持少量数据的接收

long WINAPI FishFlyCommServerStart(int port);
long WINAPI FishFlyCommServerStop();
long WINAPI FishFlyCommServerRead(char *Buf);
long WINAPI FishFlyCommServerWrite(char *Buf);

3、SocketServerDriver.h定义

#pragma once

#include "FishFlyCommServer.h"

/*************************************************
服务端在客户端被关闭和接收到客户端传送的数据时,会产生事件,提供三种处理方式:
消息模式(SKTD_MESSAGE)、回调模式(SKTD_CALLBACK)和查询模式(SKTD_QUERY)
查询模式用于服务端对客户端的单向传输模式

回调函数参数:int param,int param2,char *buf
连接关闭: param=MAKEWPARAM(Index,SKTD_CLOSE),param2=sClientSocket,buf=NULL
收到数据: param=MAKEWPARAM(Index,SKTD_RECVDATA),param2 = recvlen,buf = databuffer
Index 为客户端连接索引号(不同于Socket),databuffer为数据区缓冲

消息模式参数:wparam,lparam
连接关闭:wparam=MAKEWPARAM(Index,SKTD_CLOSE),lparam=sClientSocket 
收到数据:wparam = MAKEWPARAM(Index,SKTD_RECVDATA),lparam = buf
buf[0~3] 为缓冲区长度,buf[4~...]为数据缓冲
消息采用SendMessage形式投递

*************************************************/


typedef void (WINAPI SOCKETSERVERFUNC)(int ,int,char *);//类型(数据、状态等)、参数、缓冲区

#ifndef _SOCKETFUNC_HEAD_
#define _SOCKETFUNC_HEAD_

#define SKTD_MESSAGE  0 //消息模式
#define SKTD_CALLBACK 1 //回调函数模式
#define SKTD_QUERY    2 //查询模式(服务端时仅写入数据)

#define SKTD_CONNECTOK    0 //连接成功
#define SKTD_CONNECTFAIL  1 //连接失败
#define SKTD_CLOSE        2 //连接关闭
#define SKTD_RECVDATA     3 //接收数据
#define SKTD_SENDDATA     4 //发送数据

#endif

#pragma pack(1)

typedef struct _SOCKETSERVERSETTING {
	char   ClientIP[20]; //客户机连接IP地址,如果为空,表示可以接收任何IP地址的连接
	int    Port;         //侦听端口号
	int    ClientQueue ; //允许客户端连接数
	int    Mode;         //消息模式=0,回调函数模式=1,查询模式=2
	HWND   hWnd;         //窗体句柄
	UINT   MessageId;    //消息ID
	SOCKETSERVERFUNC *pFunc;     //回调函数句柄
	char   ConnectMsg[100]; //客户端连接时显示的问候消息
}SOCKETSERVERSETTING;

#pragma pack()

//参数Index为输出的参数,表示操作顺序号,以下其它操作均以它为参数识别句柄,即使Socket断线重连后,此索引号也保持不变
int WINAPI DCSocketServerOpen(SOCKETSERVERSETTING set,DWORD *Index); //初始化一个实例

int WINAPI DCSocketServerClose(DWORD Index); //关闭实例

int WINAPI DCSocketServerRead(DWORD ServerIndex,DWORD ClientIndex,void *buf); //读取TCP数据,返回数据长度

int WINAPI DCSocketServerWrite(DWORD ServerIndex,DWORD ClientIndex,int bufsize,void *buf);//写TCP数据

int WINAPI DCSocketServerCloseClient(DWORD ServerIndex,DWORD ClientIndex);//关闭指定的客户端连接

4、SocketServerDriver2.cpp实现

// SocketServerDriver.cpp : 定义 DLL 的初始化例程。
//

#include "stdafx.h"
//#include 
#include "FishFlyCommServer.h"

#include 		// MFC socket extensions
#include 
#include 

#pragma comment(lib,"Ws2_32.lib")

using namespace std;

#include "SocketServerDriver.h"

struct SOCKETLIST {
	DWORD ServerIndex; //服务端连接号,连接过程一直保持不变
	DWORD ClientIndex; //客户端连接号
	SOCKET sSocket; //Socket连接号,如果重连可能变化
	struct sockaddr_in local_addr; //连接地址
	int    ClientQueue ; //允许客户端连接数
	BOOL bConnect;  //Socket连接标志
	BOOL bShutdown; //Socket关闭标志
	BOOL bExitThread; //线程退出标志
	uintptr_t ThreadID; //线程ID
	HANDLE hThread;     //线程句柄
	DWORD dwRecvCount; //接收数据统计
	DWORD dwSendCount; //发送数据统计(以成功写入缓冲为准)
	DWORD dwCallRecv;  //响应接收事件次数
	DWORD dwCallSend;  //响应发送事件次数
	SOCKETSERVERSETTING Setting; //连接参数
};

#define SOCKETLISTENMAX 1
#define SOCKETCLIENTMAX 1

SOCKETLIST *SocketListenList[SOCKETLISTENMAX];//侦听数组
SOCKETLIST *SocketClientList[SOCKETCLIENTMAX];//连接数组

BOOL bSocketExitApp = FALSE; //建立线程退出标志

VOID SocketServerMainThread(LPVOID pParam);
VOID SocketClientThread(LPVOID pParam);



int SocketServerInit()
{
	WSADATA       wsd;

	// Parse the command line and load Winsock
	//

	memset(SocketListenList,0,sizeof(SocketListenList));
	memset(SocketClientList,0,sizeof(SocketClientList));

	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
	{
		MessageBox(NULL,"Failed to load Winsock library!\n","Socket",MB_OK);
		//TRACE0("SocketServerInit: Failed to load Winsock library!");
		return 1;
	}
	//
	// Create the socket, and attempt to connect to the server
	//
	return 0;
}

int SocketServerStop()
{
	//关闭服务侦听部分
    for ( int i = 0; i < SOCKETLISTENMAX ; i ++ )
	{
		if ( SocketListenList[i] != NULL )
		    DCSocketServerClose(SocketListenList[i]->ServerIndex);
	}

	WSACleanup();		
	return 0;
}



//初始化一个实例
int WINAPI DCSocketServerOpen(SOCKETSERVERSETTING set,DWORD *Index) 
{
    static DWORD LastIndex = 0;
	int ListenFind = -1;

	try{
		DWORD newIndex = GetTickCount();
		if ( newIndex <= LastIndex )
			newIndex = LastIndex +1;
		*Index = newIndex;
		LastIndex = newIndex;

		for ( int i = 0; i < SOCKETLISTENMAX; i ++ ) 
		{
			if ( SocketListenList[i] == NULL ){
			   ListenFind = i;
			   break;
			}
		}
		if ( ListenFind == -1 )
			return 0;

		//加入维护对队
		SOCKETLIST *pList = new SOCKETLIST();
		ZeroMemory(pList,sizeof SOCKETLIST);
		pList->Setting = set;
		pList->ServerIndex = *Index;
		pList->bShutdown = FALSE;
		pList->bExitThread = FALSE;

		pList->sSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (pList->sSocket == INVALID_SOCKET){
			*Index = 0;
			return 0;
		}

		ZeroMemory(&pList->local_addr,sizeof pList->local_addr);
		pList->local_addr.sin_family = AF_INET;
		pList->local_addr.sin_port = htons(pList->Setting.Port);
		if ( atoi(pList->Setting.ClientIP) == 0 )
			pList->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		else
			pList->local_addr.sin_addr.s_addr = inet_addr((char *)(pList->Setting.ClientIP));

		if ( bind(pList->sSocket,(struct sockaddr *)&(pList->local_addr),sizeof pList->local_addr) == SOCKET_ERROR ) {
			*Index = 0;
		}else 

		{
			pList->hThread = NULL;

			SocketListenList[ListenFind] = pList;


			pList->ThreadID = _beginthread(SocketServerMainThread,0,pList);
		}
	}
	catch (...) {
	}

	return 0;
}
//关闭实例
int WINAPI DCSocketServerClose(DWORD Index)
{
	int ret = -1;
	int j;
	DWORD dwTimeOut = 900;//超时值:90秒

	try{

		SOCKETLIST *pList = NULL;

		//关闭服务侦听部分
		
		for ( int i = 0; i < SOCKETLISTENMAX; i ++ )
		{
			if ( SocketListenList[i] == NULL )
				continue;
			if ( SocketListenList[i]->ServerIndex == Index ){
				pList = SocketListenList[i];
				pList->bShutdown = TRUE;
				shutdown(pList->sSocket,SD_RECEIVE|SD_SEND);
				closesocket(pList->sSocket);

				for ( j = 0; j < dwTimeOut/10 && pList->bExitThread == FALSE; j ++ )
					Sleep(10);
				//if ( SocketList[i]->bExitThread == FALSE )
				//	WaitForSingleObject(SocketList[i]->hThread,dwTimeOut);

				//TRACE("%d",SocketList[i]->bExitThread);

				//以下两行执行似乎不能安全的结束线程
				//if ( SocketList[i]->bExitThread == FALSE )
				//	TerminateThread(SocketList[i]->hThread,1);

				SocketListenList[i] = NULL;
				//delete (*itListen);
				delete (pList);
				ret = 0;
				break;
			}
		}
		//删除侦听建立的SOCKET队列(可能存在多个连接)
		for ( int i = 0; i < SOCKETCLIENTMAX; i ++ )
		{
			if ( SocketClientList[i] == NULL )
				continue;
			if ( SocketClientList[i]->ServerIndex == Index ){
				pList = SocketClientList[i];
				pList->bConnect = FALSE;
				pList->bShutdown = TRUE;
				closesocket(pList->sSocket);
//				shutdown(pList->sSocket,SD_RECEIVE|SD_SEND);
				if ( pList->Setting.pFunc != NULL ){
					for ( j = 0; j < dwTimeOut/10 && pList->bExitThread == FALSE; j ++ )
						Sleep(10);
				}
				delete pList;
				SocketClientList[i];
			}
		}
	}
	catch (...) {
	}
	return ret;
}



//读取TCP数据,返回数据长度
int WINAPI DCSocketServerRead(DWORD ServerIndex,DWORD ClientIndex,void *buf)
{


	return 0;
}
//写TCP数据
int WINAPI DCSocketServerWrite(DWORD ServerIndex,DWORD ClientIndex,int bufsize,void *buf)
{
	int sendlen = 0;
	SOCKETLIST *pListClient;

	try{
		//向同一个端口的(所有或单个)客户端写数据
		for ( int i = 0; i < SOCKETCLIENTMAX; i ++ ){
			if ( SocketClientList[i] == NULL  )
				continue;
			if ( SocketClientList[i]->ServerIndex == ServerIndex ){
				pListClient = SocketClientList[i];
				if ( ClientIndex != 0 && pListClient->ClientIndex != ClientIndex )
					continue;
				sendlen = send(pListClient->sSocket, (char *)buf, bufsize, 0);

				if ( sendlen == SOCKET_ERROR ){
					DWORD err = GetLastError();
					if ( err != WSAENOBUFS && err != WSAEMSGSIZE && err != WSAEHOSTUNREACH ){
						if ( pListClient->Setting.pFunc != NULL && pListClient->bConnect == TRUE){
							pListClient->bConnect = FALSE;
							closesocket(pListClient->sSocket);
							SocketClientList[i] = NULL;
							delete pListClient;
						}
					}
					continue;
				}

				pListClient->dwCallSend ++;
				pListClient->dwSendCount += sendlen;
			}
		}
	}
	catch (...) {
	}
	return sendlen;
}

//关闭指定的客户端连接
int WINAPI DCSocketServerCloseClient(DWORD ServerIndex,DWORD ClientIndex)
{
	int CloseCount = 0;
	SOCKETLIST *pListClient;

	try{
		for (int i = 0; i < SOCKETCLIENTMAX ; i ++ ){
			if ( SocketClientList[i] == NULL )
				continue;
			if ( SocketClientList[i]->ServerIndex == ServerIndex ){
				pListClient = SocketClientList[i];
				if ( ClientIndex != 0 && pListClient->ClientIndex != ClientIndex )
					continue;
				closesocket(pListClient->sSocket);
				CloseCount ++;
			}
		}
	}
	catch (...) {
	}
	return CloseCount;
}

//主侦听端口线程
VOID SocketServerMainThread(LPVOID pParam)
{
	SOCKETLIST *pList = (SOCKETLIST *)pParam;

	pList->hThread = GetCurrentThread();

	listen(pList->sSocket,pList->ClientQueue);
	pList->bExitThread = FALSE;

	SOCKET sClient;
	struct sockaddr_in Client_addr;
	int addrsize;
	int Index,i;

	try{
		while (!pList->bShutdown) {
			addrsize = sizeof (Client_addr);
			sClient = accept(pList->sSocket,(struct sockaddr *)&Client_addr,&addrsize);

			if ( sClient == SOCKET_ERROR )
				continue;

			i = 0; Index = 0;
			
			for ( int i = 0; i < SOCKETCLIENTMAX ; i ++ ){
				if ( SocketClientList[i] == NULL )
					continue;
				if ( SocketClientList[i]->ServerIndex != pList->ServerIndex )
					continue;
				int kk =  SocketClientList[i]->ClientIndex ;
				if ( SocketClientList[i]->ClientIndex != Index + 1 )
					break;
				Index ++;
			}
			if ( Index >= SOCKETCLIENTMAX ) {
				closesocket(sClient);
				continue;
			}

			SOCKETLIST *pListClient = new SOCKETLIST;
			memcpy(pListClient,pList,sizeof SOCKETLIST);
			pListClient->sSocket = sClient;
			pListClient->bConnect = TRUE;
			pListClient->ClientIndex = Index +1;
			SocketClientList[Index] = pListClient; //****

			if ( pListClient->Setting.Mode == SKTD_MESSAGE )
				SendMessage(pListClient->Setting.hWnd,pListClient->Setting.MessageId,MAKEWPARAM(pListClient->ClientIndex,SKTD_CONNECTOK),pListClient->sSocket);
			else if ( pListClient->Setting.Mode == SKTD_CALLBACK )
				pListClient->Setting.pFunc(MAKEWPARAM(pListClient->ClientIndex,SKTD_CONNECTOK),pListClient->sSocket,NULL);

			int len = strlen(pListClient->Setting.ConnectMsg);
			if ( len > 0 )
				send(sClient,pListClient->Setting.ConnectMsg,len,0);

			if ( pListClient->Setting.Mode != SKTD_QUERY )
				_beginthread(SocketClientThread,0,(void *)pListClient);
		}
	}
	catch (...) {
		;//AfxMessageBox("SocketServerDriver SocketServerMainThread 出错");
	}

	closesocket(pList->sSocket);
	pList->bExitThread = TRUE;

	//TRACE0("======SocketServerDriver SocketServerMainThread Exit!======\n");
}


//客户端数据接收线程
VOID SocketClientThread(LPVOID pParam)
{
	SOCKETLIST *pListClient = (SOCKETLIST *)pParam;
	char buf[64000];
	int Count = 100;
	//	int sendlen = 0;
	int recvlen = 0;
	//	SYSTEMTIME st;

	pListClient->bExitThread = FALSE;
	while (pListClient->bShutdown == FALSE) {
		//Sleep(10);
		//Count ++;
		//if ( Count < 100 )
		//	continue;
		//Count = 0;

		ZeroMemory(&buf,sizeof buf);
		recvlen = recv(pListClient->sSocket,(char *)&buf[4],sizeof(buf)-4,0);//读取包头
		if ( pListClient->bShutdown == TRUE ) //如果连接关闭,直接返回
			break ;
		if ( recvlen <= 0 ) {//socket 已经关闭 or 读取失败(缓冲为空)
			//						if ( ihead == 0 ) {
			pListClient->bConnect = FALSE;
			if ( pListClient->Setting.Mode == SKTD_MESSAGE )
				SendMessage(pListClient->Setting.hWnd,pListClient->Setting.MessageId,MAKEWPARAM(pListClient->ClientIndex,SKTD_CLOSE),pListClient->sSocket);
			else if ( pListClient->Setting.Mode == SKTD_CALLBACK )
				pListClient->Setting.pFunc(MAKEWPARAM(pListClient->ClientIndex,SKTD_CLOSE),pListClient->sSocket,NULL);
			break;
			//						}
		}else {//提交本次处理
			CopyMemory(&buf[0],&recvlen,4);
			if ( pListClient->Setting.Mode == SKTD_MESSAGE )
				SendMessage(pListClient->Setting.hWnd,pListClient->Setting.MessageId,MAKEWPARAM(pListClient->ClientIndex,SKTD_RECVDATA),(LPARAM)&buf);
			else if ( pListClient->Setting.Mode == SKTD_CALLBACK )
				pListClient->Setting.pFunc(MAKEWPARAM(pListClient->ClientIndex,SKTD_RECVDATA),recvlen,(char *)&buf[4]);
		}

		//	
		//GetLocalTime(&st);
		//sprintf(buf,"%04d-%02d-%02d %02d:%02d:%02d\r\n",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond );
		//sendlen = send(pListClient->sSocket,buf,strlen(buf),0);
		//if ( sendlen == SOCKET_ERROR ){
		//	DWORD err = GetLastError();
		//	if ( err == WSAENOBUFS || err == WSAEMSGSIZE || err == WSAEHOSTUNREACH )
		//		continue;
		//	else
		//		break;
		//}
	}

	pListClient->bExitThread = TRUE;
	if ( pListClient->bConnect == TRUE ){ //关闭连接
		pListClient->bConnect = FALSE;
		closesocket(pListClient->sSocket);
	}

	if ( pListClient->bShutdown != TRUE ) { //如果Shutdown,由shutdown方法删除数据
		for ( int i = 0; i < SOCKETCLIENTMAX; i ++ ){//删除数据
			if ( SocketClientList[i] == pListClient ){
				SocketClientList[i] = NULL;
				break;
			}
		}
		delete pListClient;
	}
}

5、资源下载

      源代码和编译后的DLL见附件。采用VS2005编译的原因是具备最广泛的操作系统支持和良好的兼容性。

     资源下载地址:https://download.csdn.net/download/yuming/88659039?spm=1001.2014.3001.5501

你可能感兴趣的:(Socket,Server,DLL)