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 <afxdllx.h>
#include "FishFlyCommServer.h"
#include <winsock2.h> // MFC socket extensions
#include <process.h>
#include <vector>
#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