一、端口互斥操作类
#ifndef _LOCK_H_
#define _LOCK_H_
#include <windows.h>
//this can be used in inter process situation.
class InterLock
{
public:
InterLock()
{
InitializeCriticalSection(&cs_);
}
~InterLock()
{
DeleteCriticalSection(&cs_);
}
void enter()
{
EnterCriticalSection(&cs_);
}
void leave()
{
LeaveCriticalSection(&cs_);
}
private:
CRITICAL_SECTION cs_;
};
template <typename LockType>
class AutoLock
{
public:
AutoLock(LockType& lock) : lock_(lock)
{
lock_.enter();
}
~AutoLock()
{
lock_.leave();
}
private:
LockType& lock_;
};
#endif
二、服务端
2.1 头文件:SocketServer.h
只维护一个客户端,心跳超多10S没有连接就断开
#ifndef _SOCKETSERVER_H_
#define _SOCKETSERVER_H_
#define MAX_FRAME_BUF 100*1024
class CSocketServer
{
private:
SOCKET listenSocket;
static SOCKET acceptSocket;
HANDLE acceptthread;
volatile bool acceptthreadrunnable;
HANDLE rcvshakehandthread;
volatile bool rcvshakehandthreadrunnable;
private:
BYTE srcframeBuf[MAX_FRAME_BUF];
BYTE dstframeBuf[MAX_FRAME_BUF];
int dstframeBufLen;
static unsigned int currentFrameLen ;
static int iState;
static DWORD lastshakehandTime;
private:
CSocketServer(void);
static CSocketServer* instance;
public:
static CSocketServer* GetInstance();
~CSocketServer(void);
bool Start(unsigned int port);
bool Stop();
bool SndMessage(const unsigned char* buf, int buflen);
bool RcvMessage(unsigned char* buf, int buflen, int& rcvlen);
void SetRcvShaveHand();
private:
static DWORD WINAPI acceptThread(LPVOID lpParameter);
static DWORD WINAPI rcvshakeHandThread(LPVOID lpParameter);
public:
void closeSocket(SOCKET& s);
};
#endif
2.2 实现端:SocketServer.cpp
CSocketServer::CSocketServer(void)
{
listenSocket = INVALID_SOCKET;
acceptthreadrunnable = false;
acceptthread = NULL;
rcvshakehandthreadrunnable = false;
rcvshakehandthread = NULL;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != NO_ERROR)
{
//printf("Error at WSAStartup()\n");
}
}
CSocketServer::~CSocketServer(void)
{
WSACleanup();
}
void CSocketServer::closeSocket(SOCKET& s)
{
if (s != INVALID_SOCKET && s != SOCKET_ERROR)
{
shutdown(s, SD_BOTH);
closesocket(s);
s = INVALID_SOCKET;
}
}
SOCKET CSocketServer::acceptSocket = INVALID_SOCKET;
unsigned int CSocketServer::currentFrameLen = 0;
int CSocketServer::iState = 1;
DWORD CSocketServer::lastshakehandTime = -1;
CSocketServer* CSocketServer::instance = new CSocketServer();
CSocketServer* CSocketServer::GetInstance()
{
return instance;
}
bool CSocketServer::Start(unsigned int port)
{
listenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if (listenSocket == INVALID_SOCKET)
{
return false;
}
sockaddr_in saServer;
saServer.sin_family = AF_INET;
saServer.sin_addr.s_addr = htonl(INADDR_ANY);
//saServer.sin_addr.s_addr = inet_addr( "192.168.1.150");
saServer.sin_port = htons(port);
if ( bind( listenSocket, (SOCKADDR*) &saServer, sizeof(saServer) ) == SOCKET_ERROR )
{
return false;
}
if ( listen( listenSocket, 1 ) == SOCKET_ERROR )
{
return false;
}
acceptthreadrunnable = true;
acceptthread = CreateThread(NULL, 0, acceptThread, this, 0, NULL);
lastshakehandTime = -1;
rcvshakehandthreadrunnable = true;
rcvshakehandthread = CreateThread(NULL, 0, rcvshakeHandThread, this, 0, NULL);
if (gbPDACommStatus)
{
WriteDebugInfo("start success\n");
}
return true;
}
DWORD WINAPI CSocketServer::rcvshakeHandThread(LPVOID lpParameter)
{
CSocketServer* server = (CSocketServer*)lpParameter;
while ( server->rcvshakehandthreadrunnable)
{
if (lastshakehandTime != -1 && GetTickCount() - lastshakehandTime > 10*1000)
{
server->closeSocket(acceptSocket);
if (gbPDACommStatus)
{
WriteDebugInfo("disconnect\n");
}
}
Sleep(3000);
}
return 0;
}
DWORD WINAPI CSocketServer::acceptThread(LPVOID lpParameter)
{
CSocketServer* server = (CSocketServer*)lpParameter;
sockaddr_in acceptAddress;
int adressLen;
SOCKET tmpSocket = INVALID_SOCKET;
while ( server->acceptthreadrunnable)
{
adressLen = sizeof(acceptAddress);
tmpSocket = accept( server->listenSocket, (SOCKADDR*) &acceptAddress, &adressLen );
if (acceptSocket == INVALID_SOCKET || acceptSocket == SOCKET_ERROR)
{
if (gbPDACommStatus)
{
WriteDebugInfo("new connect\n");
}
acceptSocket = tmpSocket;
lastshakehandTime = GetTickCount();
currentFrameLen = 0;
iState = 1;
}
else
{
server->closeSocket(tmpSocket);
}
}
return 0;
}
bool CSocketServer::Stop()
{
acceptthreadrunnable = false;
rcvshakehandthreadrunnable = false;
closeSocket(listenSocket);
closeSocket(acceptSocket);
WaitForSingleObject(acceptthread, 5000);
WaitForSingleObject(rcvshakehandthread, 5000);
if (acceptthread != NULL)
{
CloseHandle(acceptthread);
}
if (rcvshakehandthread != NULL)
{
CloseHandle(rcvshakehandthread);
}
if (gbPDACommStatus)
{
WriteDebugInfo("stop server\n");
}
return true;
}
InterLock serverSndLock;
bool CSocketServer::SndMessage(const unsigned char* buf, int buflen)
{
AutoLock<InterLock> autoLock(serverSndLock);
if (acceptSocket == INVALID_SOCKET || acceptSocket == SOCKET_ERROR)
{
return false;
}
int rc = send(acceptSocket, (char*)(buf), buflen, 0);
if(rc == SOCKET_ERROR)
{
if (gbPDACommStatus)
{
WriteDebugInfo("snd error\n");
}
closeSocket(acceptSocket);
return false;
}
if (gbPDACommData)
{
WriteDebugInfo("snd %d\n", buflen);
WriteDebugInfo(buflen, buf);
WriteDebugInfo("\n");
}
return true;
}
InterLock serverRcvLock;
bool CSocketServer::RcvMessage(unsigned char* buf, int buflen, int& rcvlen)
{
AutoLock<InterLock> autoLock(serverRcvLock);
if (acceptSocket == INVALID_SOCKET || acceptSocket == SOCKET_ERROR)
{
return false;
}
fd_set readfds;
struct timeval timeout;
timeout.tv_sec = 1000 / 1000u;
timeout.tv_usec = (1000 % 1000u) * 1000u;
FD_ZERO(&readfds);
FD_SET(acceptSocket, &readfds);
int rc = select(-1, &readfds, NULL, NULL, &timeout);
if (rc == SOCKET_ERROR)
{
if (gbPDACommStatus)
{
WriteDebugInfo("select error\n");
}
closeSocket(acceptSocket);
return false;
}
else if (rc == 0)
{
rcvlen = 0;
return false;
}
rc = recv(acceptSocket, (char*)buf, buflen, 0);
if (rc == SOCKET_ERROR || rc == 0)
{
if (gbPDACommStatus)
{
WriteDebugInfo("recv error\n");
}
closeSocket(acceptSocket);
return false;
}
if (gbPDACommData)
{
WriteDebugInfo("rcv %d\n", rc);
WriteDebugInfo(rc, buf);
WriteDebugInfo("\n");
}
rcvlen = rc;
return true;
}
void CSocketServer::SetRcvShaveHand()
{
lastshakehandTime = GetTickCount();
}
2.3 对外发布接口封装
bool WINAPI TCPServer_Start(unsigned short port)
{
if ( port < 0 || port > 65535 )
{
return false;
}
return CSocketServer::GetInstance()->Start(port);
}
bool WINAPI TCPServer_Stop()
{
return CSocketServer::GetInstance()->Stop();
}
bool WINAPI TCPServer_Send(const unsigned char* buf, int buflen)
{
if ( buf == NULL)
{
return false;
}
return CSocketServer::GetInstance()->SndMessage(buf, buflen);
}
bool WINAPI TCPServer_Recv(unsigned char* buf, int buflen, int& rcvlen)
{
if ( buf == NULL)
{
return false;
}
return CSocketServer::GetInstance()->RcvMessage(buf, buflen, rcvlen);
}
void WINAPI SetRcvShaveHand()
{
CSocketServer::GetInstance()->SetRcvShaveHand();
}
三、客户端
3.1 头文件:SocketClient.h
#ifndef _SOCKETCLIENT_H_
#define _SOCKETCLIENT_H_
class CSocketClient
{
private:
SOCKET m_socket;
public:
static CSocketClient* GetInstance();
~CSocketClient(void);
bool Connect(const char* ipAddress,unsigned short port);
bool DisConnect();
bool SndMessage(const unsigned char* buf, int buflen);
bool RcvMessage(unsigned char* buf, int buflen, int& rcvlen);
private:
CSocketClient(void);
static CSocketClient* instance;
};
#endif
3.2 实现:SocketClient.cpp
CSocketClient::CSocketClient(void)
{
m_socket = INVALID_SOCKET;
}
CSocketClient::~CSocketClient(void)
{
DisConnect();
}
CSocketClient* CSocketClient::instance = new CSocketClient();
CSocketClient* CSocketClient::GetInstance()
{
return instance;
}
bool CSocketClient::Connect(const char* ipAddress,unsigned short port)
{
if (m_socket != INVALID_SOCKET)
{
return false;
}
int Flag;
WSADATA wsaData;
struct sockaddr_in Sin;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != NO_ERROR)
{
printf("WSAStartup failed with error: %d\n", iResult);
return false;
}
m_socket = socket(AF_INET, SOCK_STREAM, 0);
if(INVALID_SOCKET == m_socket)
{
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return false;
}
memset(&Sin, 0, sizeof Sin);
Sin.sin_family = AF_INET;
Sin.sin_addr.s_addr = inet_addr(ipAddress);
Sin.sin_port = htons(port);
iResult = connect(m_socket, (struct sockaddr *)&Sin, sizeof(Sin));
if( 0 > iResult )
{
closesocket(m_socket);
WSACleanup();
m_socket = INVALID_SOCKET;
return false;
}
setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&Flag, sizeof Flag);
return true;
}
bool CSocketClient::DisConnect()
{
if (m_socket == INVALID_SOCKET)
{
return true;
}
shutdown(m_socket,SD_BOTH);
closesocket(m_socket);
WSACleanup();
m_socket = INVALID_SOCKET;
return true;
}
InterLock clientSndLock;
bool CSocketClient::SndMessage(const unsigned char* buf, int buflen)
{
AutoLock<InterLock> autoLock(clientSndLock);
if (m_socket == INVALID_SOCKET)
{
return false;
}
int havesnd = 0;
while (havesnd < buflen)
{
int rc = send(m_socket, (char*)(buf + havesnd), buflen-havesnd, 0);
if(rc == SOCKET_ERROR)
{
return false;
}
havesnd += buflen;
}
return true;
}
InterLock clientRcvLock;
bool CSocketClient::RcvMessage(unsigned char* buf, int buflen, int& rcvlen)
{
AutoLock<InterLock> autoLock(clientRcvLock);
if (m_socket == INVALID_SOCKET)
{
return false;
}
fd_set readfds;
struct timeval timeout;
timeout.tv_sec = 1000 / 1000u;
timeout.tv_usec = (1000 % 1000u) * 1000u;
FD_ZERO(&readfds);
FD_SET(m_socket, &readfds);
int rc = select(-1, &readfds, NULL, NULL, &timeout);
if (rc < 0)
{
return false;
}
else if (rc == 0)
{
rcvlen = 0;
return 0;
}
rc = recv(m_socket, (char*)buf, buflen, 0);
if (rc <= 0)
{
return false;
}
rcvlen = rc;
return 0;
}
3.3 接口封装
bool WINAPI TCPClient_Connect(const char* ipAddress,unsigned short port)
{
if (ipAddress == NULL)
{
return false;
}
if (CCommonFunc::GetInstance()->isRightIPAddress(ipAddress) != 0)
{
return false;
}
if ( port < 0 || port > 65535 )
{
return false;
}
return CSocketClient::GetInstance()->Connect(ipAddress, port);
}
bool WINAPI TCPClient_DisConnect()
{
return CSocketClient::GetInstance()->DisConnect();
}
bool WINAPI TCPClient_Send(const unsigned char* buf, int buflen)
{
if ( buf == NULL)
{
return false;
}
return CSocketClient::GetInstance()->SndMessage(buf, buflen);
}
bool WINAPI TCPClient_Recv(unsigned char* buf, int buflen, int& rcvlen)
{
if ( buf == NULL)
{
return false;
}
return CSocketClient::GetInstance()->RcvMessage(buf, buflen, rcvlen);
}