1.使用
#include "stdafx.h"
#include "AnTcp.h"
int main()
{
AnTcp *pTcp = AnTcp::GetTcp();
pTcp->SetMode(AnTCPMode::MODE_CLIENT);
pTcp->Open(9000, "127.0.0.1");
bool FirstRun = true;
while (true)
{
if (pTcp->getVisible() && FirstRun)
{
FirstRun = false;
pTcp->SendMsg("Ready", 5);
pTcp->SendMsg("AF AB FF",8, AnTCPEncode::ENCODE_HEX);
}
if (pTcp->getVisible() && !FirstRun)
{
char buffer[1024];
memset(buffer, NULL, 1024);
int size = 0;
pTcp->RecvMsg(buffer, size,AnTCPEncode::ENCODE_HEX);
if (size > 0)
{
printf("%s\n", buffer);
}
}
}
}
2.类
AnTcp.h
#pragma once
//vs报错时请在属性-->C/C++-->预处理器里填写_WINSOCK_DEPRECATED_NO_WARNINGS
#include <string>
#include <iostream>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")
enum AnTCPError
{
TCP_ERROR = -1,
TCP_SUCCESS = 0,
TCP_MODEERROR,
TCP_PARAMERROR,
TCP_BINDERROR,
TCP_LISTENERROR
};
enum AnTCPMode
{
MODE_UNUSE=-1,
MODE_CLIENT,
MODE_SERVER
};
enum AnTCPEncode
{
ENCODE_ASCII=0,
ENCODE_HEX
};
class AnTcp
{
protected:
AnTcp();
~AnTcp();
public:
//获取静态指针
static AnTcp *GetTcp();
//tcp通信模式,这个必须在open之前调用
int SetMode(AnTCPMode mode);
int GetMode(AnTCPMode &mode);
//打开和关闭tcp功能
int Open(int Port, const char *Ip = nullptr);
int Close();
//消息,接收和发送,可以选择ascii码或者16进制
int SendMsg(const char *msg, int size, AnTCPEncode code= AnTCPEncode::ENCODE_ASCII);
int RecvMsg(char *msg,int &size, AnTCPEncode code = AnTCPEncode::ENCODE_ASCII);
//是否可用,对于服务器来说有链接,对于客户端链接服务器
bool getVisible() { return m_IsSockEnable;}
protected:
void Connecting();//客户端链接服务器线程
void Waitting();//服务器等待客户端链接线程
static DWORD WINAPI ThreadOpen(LPVOID lpThreadParameter);
private:
//socket info
SOCKET m_ServerSock;
SOCKET m_ClientSock;
WSADATA m_WsaData;
sockaddr_in m_SockAddr;
//mode
AnTCPMode m_TCPMode;
//param
bool m_IsOpened;
bool m_IsSockEnable;
};
AnTcp.cpp
#include "stdafx.h"
#include "AnTcp.h"
#include <algorithm>
//socket状态检测
bool SocketCheck(SOCKET sock);
//16进制样式的ascii码字符串转16进制
//例,Ascii2Hex("AF 01 0A");
std::string Ascii2Hex(const char *ascii);
//16进制字符串转ascii码,autoSpace:自动追加空格
std::string Hex2Ascii(const char *hex,bool autoSpace=true);
AnTcp::AnTcp()
{
WSAStartup(MAKEWORD(2, 2), &m_WsaData);
m_ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
m_ClientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&m_SockAddr, 0, sizeof(m_SockAddr));
//init data
m_TCPMode = AnTCPMode::MODE_UNUSE;
m_IsOpened = false;
m_IsSockEnable = false;
}
AnTcp::~AnTcp()
{
Close();
}
AnTcp * AnTcp::GetTcp()
{
static AnTcp tcp;
return &tcp;
}
int AnTcp::SetMode(AnTCPMode mode)
{
m_TCPMode = mode;
m_SockAddr.sin_family = AF_INET;//把这个放在这里主要是想让你一定要调用这个
return TCP_SUCCESS;
}
int AnTcp::GetMode(AnTCPMode &mode)
{
mode = m_TCPMode;
return TCP_SUCCESS;
}
int AnTcp::Open(int Port, const char * Ip)
{
if (m_TCPMode == AnTCPMode::MODE_UNUSE)
return TCP_MODEERROR;
if (m_IsOpened)
return TCP_SUCCESS;
AnTCPError error= AnTCPError::TCP_ERROR;
//
switch (m_TCPMode)
{
case MODE_UNUSE:
error = AnTCPError::TCP_ERROR;
break;
case MODE_CLIENT:
{
if (!Ip|| Port<0)
{
error = AnTCPError::TCP_PARAMERROR;
break;
}
m_SockAddr.sin_port = htons(Port);
m_SockAddr.sin_addr.s_addr = inet_addr(Ip);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadOpen, this, 0, NULL);
error = AnTCPError::TCP_SUCCESS;
break;
}
case MODE_SERVER:
{
m_SockAddr.sin_port = htons(Port);
m_SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (::bind(m_ServerSock, (sockaddr*)&m_SockAddr, sizeof(m_SockAddr)) == SOCKET_ERROR)
{
error= AnTCPError::TCP_BINDERROR;
break;
}
if (listen(m_ServerSock, 2) == SOCKET_ERROR)
{
error = AnTCPError::TCP_LISTENERROR;
break;
}
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadOpen, this, 0, NULL);
error = AnTCPError::TCP_SUCCESS;
break;
}
default:
break;
}
if (error== AnTCPError::TCP_SUCCESS)
{
m_IsOpened = true;
}
return error;
}
int AnTcp::Close()
{
closesocket(m_ServerSock);
closesocket(m_ClientSock);
//终止 DLL 的使用
WSACleanup();
return TCP_SUCCESS;
}
int AnTcp::SendMsg(const char * msg, int size, AnTCPEncode code)
{
if (!m_IsOpened || !m_IsSockEnable)
return TCP_ERROR;
if (m_TCPMode == AnTCPMode::MODE_UNUSE)
return TCP_MODEERROR;
//发送之前做个心跳包检测
if (!SocketCheck(m_ClientSock))
{
m_IsSockEnable = false;
return TCP_ERROR;
}
if (code ==AnTCPEncode::ENCODE_HEX)
{
std::string Hexstr = Ascii2Hex(msg);
//发送信息
::send(m_ClientSock, Hexstr.c_str(), Hexstr.length(), NULL);
return TCP_SUCCESS;
}
//发送信息
::send(m_ClientSock, msg, size, NULL);
return TCP_SUCCESS;
}
int AnTcp::RecvMsg(char * msg, int & size, AnTCPEncode code)
{
if (!m_IsOpened || !m_IsSockEnable)
return TCP_ERROR;
if (m_TCPMode == AnTCPMode::MODE_UNUSE)
return TCP_MODEERROR;
//接收之前不能做心跳包检测,否则接收不到任何消息
//先给一个定值
int MXASIZE = 10240;
if (size>MXASIZE)
{
MXASIZE = size;
size = 0;
}
if (code == AnTCPEncode::ENCODE_HEX)
{
size = ::recv(m_ClientSock, msg, MXASIZE, NULL);
if (size > 0)
{
std::string Hexstr = Hex2Ascii(msg);
memset(msg,NULL,sizeof(msg));
memcpy(msg, Hexstr.c_str(), Hexstr.length());
}
return TCP_SUCCESS;
}
//接收
size = ::recv(m_ClientSock, msg, MXASIZE, NULL);
return TCP_SUCCESS;
}
void AnTcp::Connecting()
{
while (true)
{
if (connect(m_ClientSock, (SOCKADDR*)&m_SockAddr, sizeof(SOCKADDR)) != SOCKET_ERROR)
{
m_IsSockEnable = true;
return;
}
}
}
void AnTcp::Waitting()
{
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
while (true)
{
m_ClientSock = ::accept(m_ServerSock, (SOCKADDR*)&clntAddr, &nSize);
if (m_ClientSock!=INVALID_SOCKET)
{
m_IsSockEnable = true;
}
}
}
DWORD AnTcp::ThreadOpen(LPVOID lpThreadParameter)
{
switch (((AnTcp*)lpThreadParameter)->m_TCPMode)
{
case MODE_CLIENT:
{
((AnTcp*)lpThreadParameter)->Connecting();
break;
}
case MODE_SERVER:
{
((AnTcp*)lpThreadParameter)->Waitting();
break;
}
default:
break;
}
return 0;
}
bool SocketCheck(SOCKET sock)
{
//当使用 select()函数测试一个socket是否可读时,如果select()函数返回值为1,
//且使用recv()函数读取的数据长度为0 时,就说明该socket已经断开
char buff[20];
memset(buff, NULL, sizeof(char) * 20);
struct timeval timeout = { 1, 0 };
fd_set rdfs;
FD_ZERO(&rdfs);
FD_SET(sock, &rdfs);
int re = select(sock + 1, &rdfs, NULL, NULL, &timeout);
if (re > 0)
{
int recvLen = recv(sock, buff, sizeof(buff), 0);
if (recvLen > 0)
{
//printf("socket connected\n");
return true;
}
else if (recvLen < 0)
{
if (errno == EINTR)
{
//printf("socket connected\n");
return true;
}
else
{
//printf("socket disconnected! connect again!\n");
return false;
}
}
else if (recvLen == 0)
{
//printf("socket disconnected!connect again\n");
return false;
}
}
else if (re == 0)
{
//time out
//printf("socket connected\n");
return true;
}
else if (re < 0)
{
if (errno == EINTR)
{
//printf("socket connected\n");
return true;
}
else
{
//printf("socket disconnected ! connect again!\n");
return false;
}
}
}
std::string Ascii2Hex(const char * ascii)
{
std::string result = "";
//
std::string asciiStr = ascii;
//全转换大写
std::transform(asciiStr.begin(), asciiStr.end(), asciiStr.begin(), ::toupper);
//剔除不合法字符 0-9 a-f,空格等
for (int i = 0; i<asciiStr.length();i++)
{
bool is_legal = ((asciiStr.at(i) >= '0') && (asciiStr.at(i) <= '9')) || ((asciiStr.at(i) >= 'A') && (asciiStr.at(i) <= 'F'));
if (!is_legal)
asciiStr.erase(i,1);//剔除
}
//
if (asciiStr.length() % 2 != 0)//字符应该是2的倍数
return result;
//重新组合
const char *hex = "0123456789ABCDEF";
for (int i = 0; i<asciiStr.length() / 2; i++)
{
int leftInt = strchr(hex, toupper(asciiStr.at(i * 2))) - hex;
int rightInt = strchr(hex, toupper(asciiStr.at(i * 2 + 1))) - hex;
unsigned char re = leftInt * 16 + rightInt;
result += re;
}
return result;
}
std::string Hex2Ascii(const char * hex, bool autoSpace)
{
std::string result = "";
//
std::string hexStr = hex;
//
for (int i = 0; i < hexStr.length(); i++)
{
int temp = (hexStr.at(i) & 0xF0) >> 4;
if (temp < 10)
result += (char)(temp + '0');
else
result += (char)(temp + 'A' - 10);
temp = hexStr.at(i) & 0x0F;
if (temp < 10)
result += (char)(temp + '0');
else
result += (char)(temp + 'A' - 10);
if (autoSpace)
{
if (i != hexStr.length() - 1)
{
result += " ";
}
}
}
return result;
}