1. 添加头文件
#include <winsock.h>
#pragma comment(lib, "ws2_32.lib")
服务端:
1. 定义变量
SOCKET sSocket;
2. 定义消息常量
#define MSG_SOCKET (WM_USER + 1000)
添加消息映射
BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)
//{{AFX_MSG_MAP(CXXXDlg)
//}}AFX_MSG_MAP
ON_MESSAGE(MSG_SOCKET, OnSockEvent)
END_MESSAGE_MAP()
3. 初始化SOCKET
bool CXXXDlg::InitWSASocket()
{
char szError[256] = {0};
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
int ReturnInfo = WSAStartup(wVersionRequested, &wsaData);
if (ReturnInfo != 0)
{
return false;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
sprintf(szError, "初始化SOCKET错误,%d", WSAGetLastError());
AfxMessageBox(szError);
return false;
}
return true;
}
3. 初始化的时候调用InitWSASocket,如果初始化成功则开始监听。
BOOL CXXXDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
GetDlgItem(IDC_EDIT_PORT)->SetWindowText("8977");
if (!InitWSASocket())
{
return FALSE;
}
if (!StartSocketListen())
{
OnCancel();
}
return TRUE; // return TRUE unless you set the focus to a control
}
4. 监听模块,注册事件
bool CXXXDlg::StartSocketListen()
{
GetDlgItemText(IDC_EDIT_PORT, m_StrPort);
if (m_StrPort.IsEmpty())
{
return false;
}
char szError[256] = {0};
//创建套接字
m_sSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == m_sSocket)
{
sprintf(szError, "创建SOCKET错误,%d", WSAGetLastError());
AfxMessageBox(szError);
return false;
}
//服务器地址信息
SOCKADDR_IN addrSock;
memset(&addrSock, 0x0, sizeof addrSock);
addrSock.sin_addr.S_un.S_addr = inet_addr("127.0.0.1")/*htonl(INADDR_ANY)*/;
addrSock.sin_family = AF_INET;
addrSock.sin_port = htons(atoi(m_StrPort));
//成功返回0 ,否则返回SOCKET_ERROR
if (SOCKET_ERROR == ::bind(m_sSocket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR)))
{
sprintf(szError, "绑定SOCKET错误,%d", WSAGetLastError());
AfxMessageBox(szError);
return false;
}
//注册网络事件
if (SOCKET_ERROR == WSAAsyncSelect(m_sSocket, m_hWnd, MSG_SOCKET, FD_ACCEPT | FD_READ | FD_CLOSE))
{
::closesocket(m_sSocket);
sprintf(szError, "注册网络读取事件失败,%d", WSAGetLastError());
AfxMessageBox(szError);
return false;
}
//成功返回0 ,否则返回SOCKET_ERROR
if (SOCKET_ERROR == ::listen(m_sSocket, 100))
{
sprintf(szError, "监听SOCKET错误,%d", WSAGetLastError());
AfxMessageBox(szError);
return false;
}
return true;
}
5. 当有客户端来连接的时候,系统发送消息触发以下事件。
说明:当客户端connect的时候触发服务端的FD_ACCEPT。客户端send数据的时候触发服务端的FD_READ
,客户端关闭的时候触发服务端的FD_CLOSE。
void CXXXDlg::OnSockEvent(WPARAM wParam,LPARAM lParam)
{
SOCKET sSock= static_cast<SOCKET>(wParam);
if(WSAGETSELECTERROR(lParam))
{
AfxMessageBox("OnSockEvent()接收到错误消息");
::closesocket(wParam);
return;
}
switch(WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT:
{
SOCKADDR_IN addrSock;
memset(&addrSock, 0x0, sizeof addrSock);
int Len = sizeof(addrSock);
//当客户端connect的时候全局的m_sSocket和wParam传进来的SOCKET是相同的,
//当服务端调用accept去获取客户端的SOCKET传入下一次的OnSockEvent中wParam参数中
m_cSocket = ::accept(m_sSocket, (SOCKADDR*)&addrSock, &Len);
if (INVALID_SOCKET == m_cSocket)
{
char szError[256] = {0};
sprintf(szError, "接收SOCKET错误,%d", WSAGetLastError());
AfxMessageBox(szError);
}
}
break;
case FD_READ:
{
char buff[1024] = {0};
SOCKADDR_IN addrSock;
memset(&addrSock, 0x0, sizeof addrSock);
int Len = sizeof(addrSock);
//recv不等待是因为你使用的是非阻塞socket,换而你使用阻塞socket一样需要等待。
//recv的recvfrom是可以替换使用的,只是recvfrom多了两个参数,可以用来接收对端的地址信息,
//这个对于udp这种无连接的,可以很方便地进行回复。
//而换过来如果你在udp当中也使用recv,那么就不知道该回复给谁了,如果你不需要回复的话,也是可以使用的。
//另外就是对于tcp是已经知道对端的,就没必要每次接收还多收一个地址,
//没有意义,要取地址信息,在accept当中取得就可以加以记录了。
::recv(sSock, buff, 1024, 0);
// ::recvfrom(m_cSocket, buff, 1024, 0, (SOCKADDR*)&addrSock, &Len);
static_cast<CListBox *>(GetDlgItem(IDC_LIST_LOG))->AddString(buff);
::send(sSock/*m_cSocket*/, buff, 1024, 0);
}
break;
case FD_CLOSE:
{
::shutdown(sSock, 2);
::closesocket(sSock);
}
break;
}
}
5. 关闭程序的时候
CXXXDlg::~CXXXDlg()
{
WSACleanup();
::shutdown(m_sSocket, 2);
::closesocket(m_sSocket);
}
客户端:
与服务端一样。定义SOCKET变量,添加消息常量,添加消息映射。初始化SOCKET,关闭的时候清理SOCKET(WSACleanup)
void CXXXDlg::OnButtonSend()
{
char szError[256] = {0};
SOCKET m_Socket;
//创建套接字
if (m_IsTCP)
{
m_Socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
else
{
m_Socket = ::socket(AF_INET, SOCK_STREAM, 0);
}
if(INVALID_SOCKET == m_Socket)
{
sprintf(szError, "创建SOCKET错误,%d", WSAGetLastError());
AfxMessageBox(szError);
return;
}
//服务器地址信息
SOCKADDR_IN addrSock;
addrSock.sin_addr.S_un.S_addr = inet_addr(m_StrIP)/*htonl(INADDR_ANY)*/;
addrSock.sin_family = AF_INET;
addrSock.sin_port = htons(atoi(m_StrPort));
//连接服务器
//成功返回0 ,否则返回SOCKET_ERROR
if(SOCKET_ERROR == ::connect(m_Socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR)))
{
::closesocket(m_Socket);
sprintf(szError, "连接服务端错误,%d", WSAGetLastError());
AfxMessageBox(szError);
return;
}
else
{
CString SendBuff;
char RecvBuff[1024] = {0};
GetDlgItemText(IDC_EDIT_MSG, SendBuff);
::send(m_Socket, SendBuff, SendBuff.GetLength(), 0);
::recv(m_Socket, RecvBuff, 1024, 0);
static_cast<CListBox *>(GetDlgItem(IDC_LIST_TEXT))->AddString(RecvBuff);
}
closesocket(m_Socket);
}