一、首先为界面设置:
屏蔽一些控件,所以在你初始化时,添加代码如下:
BOOL CTCP客户程序Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化代码
GetDlgItem(IDC_TEXT)->EnableWindow(FALSE); //将消息显示框,发送消息编辑框,发送按键禁用
GetDlgItem(IDC_SENDTEXT)->EnableWindow(FALSE);
GetDlgItem(IDC_SEND)->EnableWindow(FALSE);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
在CTCP客户端程序Dlg::OnInitDialog()中包含:
s = ::socket(AF_INET, SOCK_STREAM, 0);
在class CTCP客户程序Dlg : public CDialogEx()中包含
SOCKET s; //定义套接字对象
sockaddr_in addr; //定义套接字地址结构变量
初始化如图所示:
1.connect按键:
void CTCP客户程序Dlg::OnBnClickedConnect()
{
// TODO: 在此添加控件通知处理程序代码
CString str, str1;
int port;
GetDlgItem(IDC_ADDR)->GetWindowText(str);
GetDlgItem(IDC_PORT)->GetWindowText(str1);
if ( str == "" || str1 == "" )
{
MessageBox("服务器地址或者端口不能为NULL");
}
else
{
port = atoi(str1.GetBuffer(1));//将端口字符串转换为数字
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr(str.GetBuffer(1));
addr.sin_port = ntohs(port);
GetDlgItem(IDC_TEXT)->SetWindowText("正在连接服务器....\r\n");
}
if ( ::connect(s, (sockaddr*)&addr, sizeof(addr)) != SOCKET_ERROR )
{
GetDlgItem(IDC_TEXT)->GetWindowText(str);
str += "连接服务器成功!\r\n";
GetDlgItem(IDC_TEXT)->SetWindowText(str);
GetDlgItem(IDC_SENDTEXT)->EnableWindow(true);
GetDlgItem(IDC_SEND)->EnableWindow(true);
GetDlgItem(IDC_ADDR)->EnableWindow(false);
GetDlgItem(IDC_PORT)->EnableWindow(false);
}
else
{
GetDlgItem(IDC_TEXT)->GetWindowText(str);
str += "连接服务器失败! 请重试\r\n";
GetDlgItem(IDC_TEXT)->SetWindowText(str);
}
}
注:在vs2012中如果是unicode编码的话,在atoi(str1.GetBuffer(1))这会提示无法从LPCTSTR转化为const char*,最省事的办法是将编码方式选择为多字节
2.send按键
void CTCP客户程序Dlg::OnBnClickedSend()
{
// TODO: 在此添加控件通知处理程序代码
CString str, str1;
GetDlgItem(IDC_SENDTEXT)->GetWindowText(str);
if (str == "")
{
GetDlgItem(IDC_TEXT)->GetWindowTextA(str1);
str1 += "\r\n";
str1 += "消息不能为空\r\n";
GetDlgItem(IDC_TEXT)->SetWindowText(str1);
}
else
{
::send(s, str.GetBuffer(1), sizeof(str), 0);
GetDlgItem(IDC_TEXT)->GetWindowTextA(str1);
str1 += "\r\n";
str1 += str;
GetDlgItem(IDC_TEXT)->SetWindowText(str1);
}
}
LRESULT CTCP客户程序Dlg::OnSocket(WPARAM wParam, LPARAM lParam)
{
char cs[100] = {0};
if (lParam == FD_READ)
{
CString num = "";
recv(s, cs, 100, NULL);
GetDlgItem(IDC_TEXT)->GetWindowText(num);
num += "\r\n服务器说: ";
num += (LPTSTR)cs;
GetDlgItem(IDC_TEXT)->SetWindowText(num);
}
return true;
}
3.接收与显示服务端信息
这里采用异步套接字模式。
在BOOL CTCP客户程序Dlg::OnInitDialog()中初始化异步套接字模式
::WSAAsyncSelect(s, this->m_hWnd, WM_SOCKET, FD_READ);
将异步套接字处理的时间指定为读取时间,并将事件处理消息指定为WM_SOCKET,代码如下:
#define WM_SOCKET WM_USER + 100
class CTCP客户程序Dlg : public CDialogEx
{
afx_msg LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
}
BEGIN_MESSAGE_MAP(CTCP客户程序Dlg, CDialogEx)
ON_MESSAGE(WM_SOCKET,OnSocket)
END_MESSAGE_MAP()
LRESULT CTCP客户程序Dlg::OnSocket(WPARAM wParam, LPARAM lParam)
{
char cs[100] = {0};
if (lParam == FD_READ)
{
CString num = "";
recv(s, cs, 100, NULL);
GetDlgItem(IDC_TEXT)->GetWindowText(num);
num += "\r\n服务器说: ";
num += (LPTSTR)cs;
GetDlgItem(IDC_TEXT)->SetWindowText(num);
}
return true;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
TCP服务器端:
界面初始化:
在class CTCP服务器程序Dlg : public CDialogEx中定义套接字相关变量
SOCKET s, s1;
sockaddr_in addr, addr1;
int n;
在对话框初始函数中创建套接字并且将套接字绑定到本地地址
BOOL CTCP服务器程序Dlg::OnInitDialog()
{
addr.sin_family = AF_INET;
addr.sin_port = htons(75);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
s = ::socket(AF_INET, SOCK_STREAM, 0);
::bind(s, (sockaddr*)&addr, sizeof(addr));
::listen(s, 5);
::WSAAsyncSelect(s, this->m_hWnd, WM_SOCKET, FD_ACCEPT|FD_READ);
GetDlgItem(IDC_TEXT)->EnableWindow(false);
GetDlgItem(IDC_ADDR)->SetWindowText("服务器监听已经启动!");
}
TCP服务器具有监听、发送和接收数据的功能。因为服务器必须等待客户端的连接请求之后,才能实现接收和发送数据。将服务器创建的套接字设置为异步模式,并将套接字事件设置为连接和读取事件。
#define WM_SOCKET WM_USER+10
class CTCP服务器程序Dlg : public CDialogEx
{
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
}
::WSAAsyncSelect(s, this->m_hWnd, WM_SOCKET, FD_ACCEPT|FD_READ);
ON_MESSAGE(WM_SOCKET, OnSocket)
响应套接字上的相关事件:
LRESULT CTCP服务器程序Dlg::OnSocket(WPARAM wParam, LPARAM lParam)
{
CString str13;
char cs[100] = {0};
switch (lParam)
{
case FD_ACCEPT:
{
int lenth = sizeof(addr1);
s1 = ::accept(s, (sockaddr*)&addr1, &lenth);
n = n + 1;
str13.Format("有%d客户已经连接上了", n);
GetDlgItem(IDC_TEXT)->SetWindowText(str13);
str13 += ::inet_ntoa(addr1.sin_addr);
str13 += "登录\r\n";
GetDlgItem(IDC_TEXT)->SetWindowText(str13);
}
break;
case FD_READ:
{
CString num ="";
::recv(s1, cs, 100, 0);
GetDlgItem(IDC_TEXT)->GetWindowText(num);
num += "\r\n";
num += (LPCTSTR)::inet_ntoa(addr1.sin_addr);
num += "对您说: ";
num += (LPCTSTR)cs;
GetDlgItem(IDC_TEXT)->SetWindowText(num);
}
break;
}
return true;
}
添加发送功能:
void CTCP服务器程序Dlg::OnBnClickedSend()
{
// TODO: 在此添加控件通知处理程序代码
CString str = "";
GetDlgItem(IDC_SENDTEXT)->GetWindowText(str);
if (str == "")
{
MessageBox("消息不能为空!");
}
else
{
if (::send(s1, str.GetBuffer(1), sizeof(str), 0) != SOCKET_ERROR)
{
GetDlgItem(IDC_TEXT)->SetWindowText("消息已经发送到客户端!\r\n");
GetDlgItem(IDC_TEXT)->GetWindowText(str);
str += "\r\n";
GetDlgItem(IDC_TEXT)->SetWindowText(str);;
}
else
{
GetDlgItem(IDC_TEXT)->SetWindowText("发送消息失败!\r\n");
}
}
}
--------------------------------------------------------------------------------------------------------
以下是客户端与服务器连接图: