SocketBox.dll封装套接字,利用控制台程序完成服务器与客户端的通讯.
环境:VS2019
一.SocketBox.dll
//SocketBox.h
#pragma once
#ifndef SOCKET_H
#define SOCKET_H
#ifdef SOCKET_EXPORTS
#define _DLL_API _declspec(dllexport)
#else
#define _DLL_API _declspec(dllimport)
#endif
//Server InterFace
class ServerBox
{
public:
ServerBox() {};
virtual ~ServerBox() {};
virtual bool StartServer(const int iPort = 6666) = 0;
virtual void Stop() = 0;
};
//Client InterFace
class ClientBox
{
public:
ClientBox() {};
virtual ~ClientBox() {};
virtual bool GetHostIp(char* ipaddress) = 0;
virtual bool ConnectServer(LPCSTR lpstrIP, const int iPort = 6666) = 0;
virtual void Stop() = 0;
virtual void SendData(char* srcData) = 0;
};
extern "C"
{
void _DLL_API CreateServer(ServerBox** ppServer);
void _DLL_API ReleaseServer(ServerBox* pServer);
void _DLL_API CreateClient(ClientBox** ppClient, const int iClientIndex = 0);
void _DLL_API ReleaseClient(ClientBox* pClient);
}
#endif
//SocketBox.cpp
#pragma once
#include "SocketBox.h"
#define MAXCLIENTNUM 20
//Server
class ServerBoxEx : public ServerBox
{
public:
ServerBoxEx()
{
memset(m_cMsgBuffer, 0, sizeof(char) * 1944);
memset((void*)&m_addr, 0, sizeof(sockaddr_in));
memset((void*)&m_client_addr, 0, sizeof(sockaddr_in));
m_hSocket = NULL;
m_iServerPort = 0;
m_bStopServer = false;
};
~ServerBoxEx() {};
bool StartServer(const int iPort = 6666);
void Stop();
private:
char m_cMsgBuffer[1944];
bool m_bStopServer;
int m_iServerPort;
WSADATA m_wsaData;
SOCKET m_server;//服務器套接字
sockaddr_in m_addr;//本地IP地址
fd_set m_fdsock;
fd_set m_fdread;
sockaddr_in m_client_addr;//客戶端IP地址
SOCKET m_newsock;//臨時連接的套接字變量
//服務器線程
HANDLE m_hSocket;
static UINT _stdcall ServerSocketThread(PVOID p);
};
//Client
class ClientBoxEx : public ClientBox
{
public:
ClientBoxEx()
{
m_iLinkStatus = -1;
};
~ClientBoxEx() {};
bool GetHostIp(char* ipaddress);
bool ConnectServer(LPCSTR lpstrIP, const int iPort = 6666);
void Stop();
void SendData(char* srcData);
private:
int m_iLinkStatus;
SOCKET m_client;
sockaddr_in m_server_addr;
WSADATA m_wsaData;
};
//socket.cpp
#include "pch.h"
#include "Socket.h"
#include "ToolFunction.h"
UINT _stdcall ServerBoxEx::ServerSocketThread(PVOID p)
{
ServerBoxEx* ps = static_cast<ServerBoxEx*>(p);
//設置select等待時間
timeval tSelectTime;
tSelectTime.tv_sec = GetPrivateProfileInt(_T("Config"), _T("SelectSecTime"), 0, _T("./SocketConfig.ini"));
tSelectTime.tv_usec = GetPrivateProfileInt(_T("Config"), _T("SelectUsecTime"), 0, _T("./SocketConfig.ini"));
//準備獲取客戶端
FD_ZERO(&ps->m_fdsock);//初始化fdsock
FD_SET(ps->m_server, &ps->m_fdsock);//將監聽套接字添加到套接字集合中
int iaddr_len = sizeof(sockaddr_in);
while (true)
{
if (ps->m_bStopServer)
{
break;
}
FD_ZERO(&ps->m_fdread);//初始化read
ps->m_fdread = ps->m_fdsock;
int iSelect = select(0, &ps->m_fdread, NULL, NULL, &tSelectTime);//接收到連接或者發來的消息請求的時候都會返回,此外一直阻塞,除非有指定timeval的時間
if (iSelect > 0)
{
for (unsigned int i = 0; i < ps->m_fdsock.fd_count; i++)
{
bool bIsSet = FD_ISSET(ps->m_fdsock.fd_array[i], &ps->m_fdread);
if (bIsSet)
{
//接收請求
if (ps->m_fdsock.fd_array[i] == ps->m_server)
{
//最大fd_set只能添加FD_SETSIZE個套接字,超過直接關閉服務器
int iSetClientNum = GetPrivateProfileInt(_T("Config"), _T("ServerConfig"), FD_SETSIZE, _T("./SocketConfig.ini"));
if (iSetClientNum > FD_SETSIZE)
{
iSetClientNum = FD_SETSIZE;
}
if (ps->m_fdsock.fd_count > iSetClientNum)
{
ListOutLog(SOCKET_LOG, _T("Server is over max number,accept Client fail!"));
goto END;
}
//accept為非阻塞模式,且接收的連接請求
ps->m_newsock = accept(ps->m_server, (sockaddr*)&ps->m_client_addr, &iaddr_len);
if (ps->m_newsock != INVALID_SOCKET)
{
TCHAR tcIP[20] = { 0 };
CharToTchar(inet_ntoa(ps->m_client_addr.sin_addr), tcIP);
ListOutLog(SOCKET_LOG, _T("IP: %s has linked!."), tcIP);
FD_SET(ps->m_newsock, &ps->m_fdsock);//將新的連接套接字添加到fd_sock中
}
else
{
goto END;
}
}
else
{
//接收消息
memset(ps->m_cMsgBuffer, 0, sizeof(char) * 1944);
int size = recv(ps->m_fdsock.fd_array[i], ps->m_cMsgBuffer, sizeof(ps->m_cMsgBuffer), 0);
getpeername(ps->m_fdsock.fd_array[i], (sockaddr*)&ps->m_client_addr, &iaddr_len);//獲取對方的IP地址
TCHAR tcIP[20] = { 0 };
CharToTchar(inet_ntoa(ps->m_client_addr.sin_addr), tcIP);
if (size <= 0)
{
ListOutLog(SOCKET_LOG, _T("Client [IP:%s] has been closed!"), tcIP);
closesocket(ps->m_fdsock.fd_array[i]);
FD_CLR(ps->m_fdsock.fd_array[i], &ps->m_fdsock);
}
else
{
TCHAR tcMsg[1944] = { 0 };
CharToTchar(ps->m_cMsgBuffer, tcMsg);
ListOutLog(SOCKET_LOG, _T("Get message from [IP:%s]: %s"), tcIP, tcMsg);
}
}
}
}
}
//else
//{
// break;
//}
}
END:
for (unsigned int i = 0; i < ps->m_fdsock.fd_count; i++)
{
closesocket(ps->m_fdsock.fd_array[i]);
}
//關閉服務端套接字
closesocket(ps->m_server);
WSACleanup();
ListOutLog(SOCKET_LOG, _T("Prepare to close server."));
return 0;
}
bool ServerBoxEx::StartServer(const int iPort)
{
m_iServerPort = iPort;
//初始化winsock2.dll
int iWinSock = WSAStartup(MAKEWORD(2, 2), &m_wsaData);
if (iWinSock != 0)
{
ListOutLog(SOCKET_LOG, _T("init WinSock2.dll fail!"));
return false;
}
ListOutLog(SOCKET_LOG, _T("init WinSock2.dll success!."));
//創建套接字
m_server = socket(AF_INET, SOCK_STREAM, 0);
if (m_server == SOCKET_ERROR)
{
ListOutLog(SOCKET_LOG, _T("Create socket fail!Error code:%d."), WSAGetLastError());
WSACleanup();
return false;
}
ListOutLog(SOCKET_LOG, _T("Create socket success!."));
//設定套接字為非阻塞模式
unsigned long ul = 1;
int iIoct = ioctlsocket(m_server, FIONBIO, &ul);
if (iIoct == SOCKET_ERROR)
{
ListOutLog(SOCKET_LOG, _T("Set ioctlsocket fail!Error code:%d."), WSAGetLastError());
closesocket(m_server);
WSACleanup();
return false;
}
//填寫本地IP
int iaddr_len = sizeof(sockaddr_in);
memset((void*)&m_addr, 0, iaddr_len);
m_addr.sin_family = AF_INET;
m_addr.sin_port = htons(m_iServerPort);//主機字節端口轉網絡字節端口
m_addr.sin_addr.s_addr = htonl(INADDR_ANY);//允許將本機任何IP地址轉為網絡字節地址
//服務器套接字與本地IP相綁定
int iBind = bind(m_server, (sockaddr*)&m_addr, sizeof(m_addr));
if (iBind != 0)
{
ListOutLog(SOCKET_LOG, _T("bind local IP with socket fail !Error code:%d."), WSAGetLastError());
closesocket(m_server);
WSACleanup();
return false;
}
//監聽
int iListen = listen(m_server, 0);
if (iListen != 0)
{
ListOutLog(SOCKET_LOG, _T("listening fail!Error code:%d."), WSAGetLastError());
closesocket(m_server);
WSACleanup();
return false;
}
ListOutLog(SOCKET_LOG, _T("bind local IP with socket success and listen..."));
m_hSocket = (HANDLE)_beginthreadex(NULL,0, ServerSocketThread,this,0,NULL);
if (m_hSocket != NULL)
{
return true;
}
else
{
ListOutLog(SOCKET_LOG, _T("server thread fail!Error code:%d."), WSAGetLastError());
closesocket(m_server);
WSACleanup();
return false;
}
return false;
}
void ServerBoxEx::Stop()
{
if (m_hSocket != NULL)
{
m_bStopServer = true;
DWORD dwTime = WaitForSingleObject(m_hSocket, INFINITE);
CloseHandle(m_hSocket);
m_hSocket = NULL;
ListOutLog(SOCKET_LOG, _T("Stop current server success."));
}
m_bStopServer = false;
}
bool ClientBoxEx::GetHostIp(char* ipaddress)
{
int err;
int nStatus = 0;
bool bRet = true;
char szhn[256] = { 0 };
WORD wVersionRequested;
HOSTENT* host = { 0 };
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) bRet = false;
nStatus = gethostname(szhn, sizeof(szhn));
if (nStatus == SOCKET_ERROR) bRet = false;
host = gethostbyname(szhn);
char* ipTemp = NULL;
if (host != NULL)
{
ipTemp = inet_ntoa(*(IN_ADDR*)host->h_addr_list[0]);
}
strcpy_s(ipaddress, 30, ipTemp);
WSACleanup();
return bRet;
}
bool ClientBoxEx::ConnectServer(LPCSTR lpstrIP, const int iPort)
{
int addr_len = sizeof(sockaddr_in);
int iWinSock = WSAStartup(MAKEWORD(2, 2), &m_wsaData);
if (iWinSock != 0)
{
ListOutLog(SOCKETClient_LOG, _T("init WinSock2.dll fail!"));
return false;
}
ListOutLog(SOCKETClient_LOG, _T("init WinSock2.dll success!."));
//創建套接字
m_client = socket(AF_INET, SOCK_STREAM, 0);
if (m_client < 0)
{
ListOutLog(SOCKETClient_LOG, _T("Create socket fail!Error code:%d."), WSAGetLastError());
WSACleanup();
return false;
}
ListOutLog(SOCKETClient_LOG, _T("Create socket success!."));
memset((void*)&m_server_addr, 0, addr_len);
m_server_addr.sin_family = AF_INET;
m_server_addr.sin_port = htons(iPort);
in_addr a;
inet_pton(AF_INET, lpstrIP, &a);
m_server_addr.sin_addr.s_addr = a.S_un.S_addr;
TCHAR lpctstrIP[20] = { 0 };
CharToTchar((char*)lpstrIP, lpctstrIP);
//與服務器建立連接
m_iLinkStatus = connect(m_client, (sockaddr*)&m_server_addr, addr_len);
if (m_iLinkStatus != 0)
{
ListOutLog(SOCKETClient_LOG, _T("IP: %s Connect server fail!Error code:%d."), lpctstrIP, WSAGetLastError());
closesocket(m_client);
WSACleanup();
return false;
}
ListOutLog(SOCKETClient_LOG, _T("IP: %s Connect server success!."), lpctstrIP);
return true;
}
void ClientBoxEx::Stop()
{
closesocket(m_client);
WSACleanup();
}
void ClientBoxEx::SendData(char* srcData)
{
send(m_client, srcData, strlen(srcData), 0);
}
void _DLL_API CreateServer(ServerBox** ppServer)
{
*ppServer = new ServerBoxEx;
}
void _DLL_API ReleaseServer(ServerBox* pServer)
{
if (pServer != NULL)
{
delete pServer;
pServer = NULL;
}
}
void _DLL_API CreateClient(ClientBox** ppClient, const int iClientIndex)
{
*ppClient = new ClientBoxEx;
}
void _DLL_API ReleaseClient(ClientBox* pClient)
{
if (pClient != NULL)
{
delete pClient;
pClient = NULL;
}
}
二.Server服务器Demo
#include <iostream>
#include <Windows.h>
#include "../../Build/include/SocketBox.h"
#pragma comment(lib,"../../Build/lib/x64/Debug/SocketBox.lib")
int main()
{
ServerBox* sb;
CreateServer(&sb);
if (sb->StartServer(6666))
{
std::cout << "服務器啟動成功!" << std::endl;
std::cout << "--------------" << std::endl;
std::cout << "按任意鍵關閉服務器" << std::endl;
}
system("pause");
sb->Stop();
ReleaseServer(sb);
return 0;
}
三.客户端Demo
#include <iostream>
#include <Windows.h>
#include "../../Build/include/SocketBox.h"
#pragma comment(lib,"../../Build/lib/x64/Debug/SocketBox.lib")
int main()
{
char cMsg[1024] = "1";
char cLocalIP[100] = { 0 };
ClientBox* cb;
CreateClient(&cb);
cb->GetHostIp(cLocalIP);
if (cb->ConnectServer(cLocalIP, 6666))
{
std::cout << "服務器連接成功!" << std::endl;
std::cout << "--------------" << std::endl;
std::cout << "按請輸入要發送的消息" << std::endl;
std::cin >> cMsg;
cb->SendData(cMsg);
std::cout << "按請輸入要發送的消息" << std::endl;
std::cin >> cMsg;
cb->SendData(cMsg);
}
std::cout << "按任意鍵關閉客戶端" << std::endl;
system("pause");
cb->Stop();
ReleaseClient(cb);
return 0;
}
可以实现多个客户端连接并给服务端发送消息