TCP通信是面向连接的,所以需要服务器进行监听,客户端发起连接,服务器再进行接受。
1.在使用socket通信时需要是要WinSock库,所以首先调用WSAStartup( DWORD wVersionRequested, LPWSADATA lpWSAData)进行库初始化,成功的话返回TRUE.
WSADATA WSAData;
if(WSAStartup((MAKEWORD(2,0)), &WSAData) != 0)
{
return FALSE;
}
2.首先调用socket(int af, int type, int protocol)创建用于绑定的socket变量,再调用bind(SOCKET s, const struct sockadddr FAR* name, int namelen)进行IP及端口与socket变量的绑定,开始监听,监听后可以使用WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);进行相应事件的处理。
s为绑定的socket变量,hWnd为接受消息的窗口句柄,wMsg是传给窗口的消息,lEvent 为被注册的网络事件。lEvent = FD_ACCEPT表示当有连接请求时发送消息给消息处理函数进行处理,进而通知服务器去accept请求, lEvent = FD_READ时表示套接字s上有数据时发送消息通知消息处理函数进行处理,详见下面的程序。
void CData_TransmitDlg::OnBnClickedListenButton()
{
// TODO: 在此添加控件通知处理程序代码
CString strPort = _T("");
GetDlgItemText(IDC_PORT_EDIT, strPort);
if(strPort.IsEmpty())
{
AfxMessageBox(_T("输入端口号"));
return ;
}
closesocket(m_Listen);
//创建套接字
m_Listen = socket(AF_INET, SOCK_STREAM,0);//SOCK_STREAM表示TCP协议
if(m_Listen == INVALID_SOCKET)
{
AfxMessageBox(_T("创建套接字失败"));
return ;
}
WSAAsyncSelect(m_Listen, m_hWnd, WM_SOCKET, FD_ACCEPT|FD_READ);/*用于告知服务器已经检测连接要去接受连接,也可以在监听成功后采用线程去监测数据*/
//获取本地计算机的IP
CString strAddr = _T("");
GetDlgItemText(IDC_PORT_EDIT, strPort);
CString strHostname = _T("");
gethostname(strHostname.GetBuffer(MAX_PATH), MAX_PATH);
strHostname.ReleaseBuffer();
struct hostent* pHostInfo = gethostbyname(strHostname);
strAddr = inet_ntoa(*(in_addr*)pHostInfo->h_addr_list[0]);
//套接字地址
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr(strAddr);
addr.sin_port = htons(atoi(strPort));
//绑定套接字
int nRes = bind(m_Listen, (sockaddr*)&addr, sizeof(addr)) ;
if(nRes ==SOCKET_ERROR)
{
MessageBox("绑定套接字失败");
return ;
}
//开始监听
if(listen(m_Listen, 5) == 0)
{
MessageBox("监听连接成功");
}
else
{
MessageBox("监听连接失败");
}
}
3.自定义的WM_SOCKET消息处理函数,完成接受连接和处理数据:
//WM_SOCKET消息处理函数
LRESULT CData_TransmitDlg::OnSocket(WPARAM wParam,LPARAM lParam)
{
switch(lParam)
{
case FD_ACCEPT:
closesocket(m_Accept);
//接受连接
m_Accept = accept(m_Listen , NULL , NULL);//接受连接
if(m_Accept != INVALID_SOCKET)
{
MessageBox("接受连接成功");
}
else
{
MessageBox("接受连接失败");
}
break;
case FD_READ: //有客户端发来的数据
TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
int nRes = recv(m_Accept, pBuffer, 1024, 0);
if(nRes != SOCKET_ERROR)
{
SetDlgItemText(IDC_DATA_RECV_EDIT, pBuffer);
delete[] pBuffer;
break;
}
}
return 1;
}
4.服务端发送数据:
void CData_TransmitDlg::OnBnClickedSendButton()
{
// TODO: 在此添加控件通知处理程序代码
CString str = "";
GetDlgItemText(IDC_DATA_SEND_EDIT, str);
int nRes = send(m_Accept, str, 1024, 0);
if( nRes == SOCKET_ERROR)
{
MessageBox(_T("数据发送失败"));
}
}
1.向服务器发出连接请求:
void CClientDlg::OnBnClickedConnectButton()
{
// TODO: 在此添加控件通知处理程序代码
CString SrvrIp;
GetDlgItemText(IDC_IP_EDIT,SrvrIp);
if(SrvrIp.IsEmpty())
{
MessageBox(_T("输入服务器IP地址"));
return ;
}
CString strPort = _T("");
GetDlgItemText(IDC_PORT_EDIT, strPort);
if(SrvrIp.IsEmpty())
{
MessageBox(_T("输入服务器端口号"));
return ;
}
//关闭套接字
closesocket(m_Connect);
// 创建套接字
m_Connect = socket(AF_INET ,SOCK_STREAM, 0);
WSAAsyncSelect(m_Connect, m_hWnd, WM_RECV, FD_READ);
if(m_Connect == INVALID_SOCKET)
{
MessageBox(_T("创建套接字失败"));
return ;
}
//套接字地址
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr(SrvrIp);
addr.sin_port = htons(atoi(strPort));
m_SrvrAddr = addr;
//建立连接
if(0 == connect(m_Connect,(sockaddr*)&addr, sizeof(addr)))
{
MessageBox(_T("建立连接成功"));
/*
m_ThreadParam.hWnd = m_hWnd;
m_ThreadParam.str = _T("");
m_ThreadParam.socket = m_Connect;
AfxBeginThread((AFX_THREADPROC)RecvPro, &m_ThreadParam);
*/
}
else
{
MessageBox(_T("建立连接失败"));
}
}
2.处理来自服务器的数据,通过网络事件FD_READ完成,当m_Connect上有来自服务器的数据就会发送消息
// 接受数据处理
LRESULT CClientDlg::OnRecvData(WPARAM wParam,LPARAM lParam)
{
switch(lParam)
{
case FD_READ:
TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
int nRes = recv(m_Connect, pBuffer, 1024, 0);
if(nRes != SOCKET_ERROR)
{
SetDlgItemText(IDC_DRECEV_EDIT, pBuffer);
delete[] pBuffer;
break;
}
//
/*CString str = m_ThreadParam.str;
SetDlgItemText(IDC_DRECEV_EDIT, str);*/
}
return 0;
}
3.客户端进行数据发送:
void CClientDlg::OnBnClickedSendButton()
{
// TODO: 在此添加控件通知处理程序代码
CString str = "";
GetDlgItemText(IDC_DSEND_EDIT, str);
int nRes = send(m_Connect, str, 1024, 0);
if( nRes == SOCKET_ERROR)
{
MessageBox(_T("数据发送失败"));
}
}
4.补充:
如果不使用网络事件进行消息处理,也可以采用线程进行监听数据
//监测来自服务器的数据,线程函数如下
DWORD CClientDlg::RecvPro(LPVOID lParam)
{
TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
THREAD_PARAM* pThreadParam = ((THREAD_PARAM*)lParam);
HWND hwnd = pThreadParam->hWnd;
while(TRUE)
{
int nRes = recv(pThreadParam->socket, pBuffer, 1024, 0 );
if(nRes != SOCKET_ERROR)
{
pThreadParam->str = pBuffer;
::PostMessage(hwnd, WM_RECV, 0 ,0);
}
}
delete[] pBuffer;
return 0;
}
//自定义消息WM_RECV处理结果函数
// 接受数据处理
LRESULT CClientDlg::OnRecvData(WPARAM wParam,LPARAM lParam)
{
CString str = m_ThreadParam.str;
SetDlgItemText(IDC_DRECEV_EDIT, str);
}
return 0;
}
5.通信结果如下:
客户机代码:
//初始化
BOOL CServerDlg::OnInitDialog()
{
/*.....
*/
// TODO: 在此添加额外的初始化代码
m_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(m_socket==INVALID_SOCKET)
{
MessageBox("创建失败!");
return FALSE;
}
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.103");
addr.sin_port = htons(8888);//接受端口,用于接收数据
int Result = bind(m_socket,(sockaddr*)&addr, sizeof(SOCKADDR));//绑定
if(Result == SOCKET_ERROR)
{
MessageBox("套节字帮定失败!");
closesocket(m_socket);
return FALSE;
}
m_Addr1 = addr;
addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.103");
addr.sin_port = htons(5601);//发送端口
m_Addr2 = addr;
PARAM_THREAD* p = new PARAM_THREAD;
p->socket = m_socket;
p->str = "";
p->hWnd = m_hWnd;
HANDLE handle=::CreateThread(NULL,0,RecvPro,(LPVOID)p,0,NULL);//建立监听线程
CloseHandle(handle);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
//线程函数,用于监听消息
DWORD CServerDlg::RecvPro(LPVOID lParam)
{
PARAM_THREAD* pParamThread = (PARAM_THREAD*)lParam;
HWND hwnd = pParamThread->hWnd;
SOCKET socket = pParamThread->socket;
TCHAR* pBuffer = new TCHAR[1024];
memset(pBuffer, 0, 1024);
SOCKADDR_IN CliAddr;
int addr_len = sizeof(CliAddr);
while(TRUE)
{
int nRes = recvfrom(socket, pBuffer, 1024,0,(sockaddr*)&CliAddr,&addr_len);
if(nRes == SOCKET_ERROR)
{
AfxMessageBox(_T("接受数据错误"));
break;
}
pBuffer[strlen(pBuffer)+1] = '\0';
::PostMessage(hwnd, WM_RECV,(WPARAM)&CliAddr,(LPARAM)pBuffer);
}
delete[] pBuffer;
return 0;
}
//消息处理
LRESULT CServerDlg::OnRecv(WPARAM wParam, LPARAM lParam)
{
CString str = (char*)lParam;
SetDlgItemText(IDC_RECV_EDIT, str);
return 0;
}
void CServerDlg::OnBnClickedSendButton()
{
// TODO: 在此添加控件通知处理程序代码
CString str = "";
GetDlgItemText(IDC_SEND_EDIT, str);
int addr_len = sizeof(struct sockaddr_in);
sockaddr_in addr = m_Addr2;
SOCKET sdocket = socket(AF_INET, SOCK_DGRAM, 0);
int nRes = sendto(sdocket ,str, 1024, 0,(sockaddr*)&m_Addr2,addr_len);
if(nRes == SOCKET_ERROR)
{
MessageBox("数据未发送");
}
}
3. 通信结果如下: