简介:
TCP有客户端和服务端,简称Client和Server。下面就介绍如何创建以及乱码等处理。
1.服务端
1)首先要新建两个类,基类为CAsyncSocket,一个是类作用是监听,另一个类作用是重写发送和接收函数。
class CListenSocket : public CAsyncSocket
class COverwriteSocket : public CAsyncSocket
2)服务端的监听类
1)创建和监听
void CTcpAsynSDlg::OnButtonTcpServer()
{
// TODO: Add your control notification handler code here
GetDlgItem(IDC_BUTTON_TcpServer)->SetWindowText(_T("服务器打开"));
if (m_ListenSocket.m_hSocket == INVALID_SOCKET)
{
BOOL bFlag = m_ListenSocket.Create(8888, SOCK_STREAM, FD_ACCEPT); //创建
if (!bFlag)
{
AfxMessageBox("Socket创建失败!");
m_bIsTcpOpen = FALSE;
return;
}
if (!m_ListenSocket.Listen()) //监听
{
int nError = m_ListenSocket.GetLastError();
if (nError != WSAEWOULDBLOCK)
{
AfxMessageBox("Socket的Listen失败!");
m_bIsTcpOpen = FALSE;
return;
}
}
m_bIsTcpOpen = TRUE;
GetDlgItem(IDC_BUTTON_TcpServer)->SetWindowText(_T("服务端关闭"));
}
else
{
m_ListenSocket.Close();
GetDlgItem(IDC_BUTTON_TcpServer)->SetWindowText(_T("服务器打开"));
m_bIsTcpOpen = FALSE;
}
}
2)监听客户端是否连接
void CListenSocket::OnAccept(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
m_pOverwriteSocket = new COverwriteSocket();
if (Accept(*m_pOverwriteSocket)) //Listen之后监听客户端是否连接
{
g_bIsListenSocket = TRUE;
}
else
{
g_bIsListenSocket = FALSE;
delete m_pOverwriteSocket;
m_pOverwriteSocket = NULL;
}
CAsyncSocket::OnAccept(nErrorCode);
}
COverwriteSocket *m_pOverwriteSocket;
记住监听一定是另一个重写发送和接收的类,检测是否有客户端连接。
3)服务端的重写接受和发送的类
(1)重写OnReceive
void COverwriteSocket::OnReceive(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
memset(m_szRecBuffer, 0, sizeof(m_szRecBuffer));
m_nLength = Receive(m_szRecBuffer, sizeof(m_szRecBuffer)); //接收
if (m_nLength == strlen(m_szRecBuffer))
{
g_bIsOnReceive = TRUE;
}
if (m_nLength == -1)
{
g_bIsOnReceive = FALSE;
}
CAsyncSocket::OnReceive(nErrorCode);
}
(2)重写OnSend
void COverwriteSocket::OnSend(int nErrorCode)
{
// TODO: Add your specialized code here and/or call the base class
m_nLength = Send(m_chBuffer, strlen(m_chBuffer)); //发送
CAsyncSocket::OnSend(nErrorCode);
}
(3)注意:
接收和发送变量定义
char m_chBuffer[4096];
char m_szRecBuffer[4096];
如果客户端和服务端都写,接收和发送变量类型如果不一样,就要自己转换,否则会有乱码。
2.客户端
1)调用Create和Connect函数建立连接
CAsyncSocket *m_pClientSocket;
BOOL CTcpClient::TcpClientOpen()
{
int nRet = 0;
m_pClientSocket = new CAsyncSocket();
GetHostAddress(m_strIP);
if(m_pClientSocket->Create()) //创建
{
nRet = m_pClientSocket->Connect(m_strIP, 8888); //连接
Sleep(10);
int nError = WSAGetLastError();
if (nError == WSAEWOULDBLOCK) //判断是否成功
{
m_IsTcpOpen = TRUE;
return TRUE;
}
}
m_IsTcpOpen = FALSE;
return FALSE;
}
如果new CSocket()。简单判断成功的标准就是读返回值,查看MSDN或者百度或谷歌。也可通过接收和发送判断是否成功。
2)接收,可重写OnReceive也可直接调用Receive()函数
BOOL CTcpClient::TcpClientReceive(CString& strReceive, int nRecLen)
{
int nLen = 4096;
int nRet = 0;
char chBuffer[4096];
memset(chBuffer, 0, sizeof(char)*nLen);
nRet =m_pClientSocket->Receive(chBuffer, nLen); //接收
if (nRet == 0)
{
AfxMessageBox("TCP未连接!");
return FALSE;
}
else if (nRet == SOCKET_ERROR)
{
AfxMessageBox("TCP接收数据出错!");
return FALSE;
}
else
{
chBuffer[nRet] = 0;
nRecLen = nRet;
strReceive += chBuffer;
return TRUE;
}
return FALSE;
}
3)发送,可重写OnSend也可直接调用Send()函数。
BOOL CTcpClient::TcpClientSend(CString strSend)
{
int nLen = 0;
sprintf(m_chBuffer,"%s", strSend); //CString转换成char数组
m_nLength = strlen(m_chBuffer);
nLen =m_pClientSocket->Send(&m_chBuffer, strlen(m_chBuffer)); //发送
if (nLen == strSend.GetLength())
{
return TRUE;
}
return FALSE;
}
4)注意:
sprintf(m_chBuffer,"%s", strSend);
这一句转换相当重要,当自己在对话框添加编辑框,获取编辑框的内容时,一定要转换成char数组,以便和其它兼容,如果还是CString数据直接发送出去,如果对方接收函数变量定义的类型是char数组,就会一定乱码。根据自己定义的类型,使用不同的转换方法。简单理解:客户端和服务端接收发送的变量类型一开始要保持一致。
3.对话框显示
1)界面
服务端创建:放两个编辑框,三个按钮,定义接收和发送数组。
4.注意
基类可以使用CSocket,其和CAsyncSocket原理上的关系和区别:
1)CSocket类是从CAsyncSocket类派生出来的,继承了Receive、Send、Accept等函数。
2)CSocket对象执行Receive、ReceiveFrom和Accept函数时是阻塞的,而CAsyncSocket对象在执行以上函数时是非阻塞的,会返回WSAEWOULDBLOCK。
3)使用CAsyncSocket,如果通过GetLastError()返回的值如果是WSAEWOULDBLOCK,就说明成功,不要以为这是个错误值,切记!本人一开始没有理解透,浪费很多时间。
4)对于选择哪种基类,没必要太多纠结。可根据项目需求和个人意愿。
5)代码上的区别就是连接判断等函数返回值的判断。阻塞和非阻塞,简单理解就是同步和异步。
6)代码注释地方皆为主要部分。