1.程序说明
开发环境为VS2012,基于TCP连接的客户端与服务端的通信程序,服务端IP为本地网卡ip地址或127.0.0.1,默认端口为1234(在程序编写过程中连接端口要大于1000,否则容易与计算机中某些程序端口冲突导致无法通信)。
2.socket简介
MFC类库中,几乎封装了Windows Sockets的全部功能,在微软基础类库中有两个基础类:CAsyncSocket类封装了异步套接字(非阻塞模式)的基本功能;CSocket类派生于CAsyncSocket类,具有串行化功能(阻塞模式)。
CAsyncSocket类实现数据传输步骤如下:
(1)调用构造函数创建套接字对象;
(2)如果创建服务器套接字,则调用Bind()函数绑定本地IP和端口,然后调用Listen()函数监听客户端请求。如果请求到来,则调用Accept()函数响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可;
(3)调用Send()等功能函数进行数据传输与处理;
(4)关闭或销毁套接字对象。
CSocket类实现数据传输步骤如下:
(1)创建CSocket类对象;
(2)如果创建服务器端套接字,则调用Bind()函数绑定本地IP和端口,然后调用Listen()函数监听客户端请求。如果请求到来,则调用Accept()函数响应该请求。如果创建客户端套接字,则直接调用函数Connect()连接服务器即可;
(3)创建与CSocket类对象相关联的CSocketFile类对象;
(4)创建与CSocket类对象相关联的CArchive类对象;
(5)使用CArchive类对象在客户端和服务器之间进行数据传输;
(6)关闭或销毁CSocket类,CSocketFile类,CArchive类三个对象。
上述两种步骤均为使用微软类库进行开发流程,使用MFC通信开发调用MFC封装类库即可。
3.相关函数简介
(1)struct sockaddr_in
{
short sin_family;//指定地址家族即地址格式,默认为AF_INET,就是TCP/IP协议
unsigned short sin_port;//端口号
struct in_addr sin_addr;//ip地址
char sin_zero[8];//需要指定为0
}
(2)BOOL Bind(const SOCKADDR* lpSockAddr,int nSockAddrLen);
lpSockAddr指定将要绑定的服务器地址结构
nSockAddrLen地址结构的长度
(3)BOOL Listen(int nConnectionBacklog=5);
最大监听客户端数,默认为5
(4)BOOL Connect(const SOCKADDR* lpSockAddr,int nSockAddrLen);
lpSockAddr 服务器地址结构
nSockAddrLen 地址结构长度
(5)virtual int Send(const void* lpBuf,int nBufLen,int nFlags=0);
lpBuf 待发送数据地址
nBufLen 待发送数据大小
nFlags 发送标志
(6)virtual int Receive(const void* lpBuf,int nBufLen,int nFlags=0);
lpBuf 接收数据地址
nBufLen 接收数据大小
nFlags 标志
4.关键代码(1)服务器端代码
#define WM_SOCKET_SERVER WM_USER+1000 // CSocket_Connect_ServerDlg 对话框 class CSocket_Connect_ServerDlg : public CDialogEx { // 构造 public: CSocket_Connect_ServerDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_SOCKET_CONNECT_SERVER_DIALOG }; SOCKET s,s1;//定义套接字句柄 sockaddr_in addr,add1;//定义套接字地址结构变量 int n; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() afx_msg LRESULT OnSocketServer(WPARAM wParam, LPARAM lParam); public: afx_msg void OnClickedButton1(); };
BOOL CSocket_Connect_ServerDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 n=0; addr.sin_family=AF_INET; addr.sin_port=htons(1234); 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); GetDlgItem(IDC_EDIT1)->EnableWindow(FALSE); GetDlgItem(IDC_STATIC)->SetWindowTextA("服务器监听已经启动!"); ::WSAAsyncSelect(s,this->m_hWnd,WM_SOCKET_SERVER,FD_ACCEPT|FD_READ|FD_CONNECT);//设置异步套接字 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE }
afx_msg LRESULT CSocket_Connect_ServerDlg::OnSocketServer(WPARAM wParam, LPARAM lParam) { CString str13; char cs[100] = {0}; //int err_log=listen(s,10); //if (err_log != 0) //{ // perror("listen"); // //close(s); // exit(-1); //} switch (lParam) { case FD_ACCEPT: //连接事件 { int lenth=sizeof(add1); s1=::accept(s,(sockaddr*)&add1,&lenth); n=n+1; str13.Format("有%d客户已经连接上了\n",n); str13+=::inet_ntoa(add1.sin_addr); str13+="\r\n登录\r\n"; GetDlgItem(IDC_EDIT1)->SetWindowTextA(str13); } case FD_READ: //读取事件 { CString num=""; ::recv(s1,cs,100,0); GetDlgItem(IDC_EDIT1)->GetWindowTextA(num); num += "\r\n"; num += (LPTSTR)::inet_ntoa(add1.sin_addr); num += "对您说:"; num += (LPTSTR)cs; GetDlgItem(IDC_EDIT1)->SetWindowTextA(num); } default: break; } return 0; } void CSocket_Connect_ServerDlg::OnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 CString str=""; GetDlgItem(IDC_EDIT2)->GetWindowTextA(str); if (str=="") { MessageBox("消息不能为空!"); } else { if (::send(s1,str.GetBuffer(1),str.GetLength(),0)!= SOCKET_ERROR) { GetDlgItem(IDC_EDIT1)->GetWindowTextA(str); str += "\r\n消息已经发送到客户端!\r\n"; GetDlgItem(IDC_EDIT1)->SetWindowTextA(str); } else { GetDlgItem(IDC_EDIT1)->SetWindowTextA("消息发送失败!\r\n"); } } }
(2)客户端代码#define WM_SOCKET WM_USER+100 // CSocket_ConnectDlg 对话框 class CSocket_ConnectDlg : public CDialogEx { // 构造 public: CSocket_ConnectDlg(CWnd* pParent = NULL); // 标准构造函数 // 对话框数据 enum { IDD = IDD_SOCKET_CONNECT_DIALOG }; SOCKET s; sockaddr_in addr; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 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() public: afx_msg void OnClickedButton1(); afx_msg void OnClickedButton2(); };
BOOL CSocket_ConnectDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 this->GetDlgItem(IDC_EDIT3)->EnableWindow(FALSE); this->GetDlgItem(IDC_EDIT4)->EnableWindow(FALSE); this->GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE); s=::socket(AF_INET,SOCK_STREAM,0); //创建套接字 ::WSAAsyncSelect(s,this->m_hWnd,WM_SOCKET,FD_READ);//将套接字设置为异步模式 return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CSocket_ConnectDlg::OnClickedButton1() { // TODO: 在此添加控件通知处理程序代码 CString str,str1; int port; GetDlgItem(IDC_EDIT1)->GetWindowTextA(str); GetDlgItem(IDC_EDIT2)->GetWindowTextA(str1); if (str==""||str1=="") { MessageBox("服务器地址或端口号不能为空"); } else { port=atoi(str1.GetBuffer(1));//将端口字符串转换为数字 addr.sin_family=AF_INET; addr.sin_addr.S_un.S_addr=inet_addr(str.GetBuffer(1)); //转换服务器IP地址 addr.sin_port=ntohs(port); GetDlgItem(IDC_EDIT3)->SetWindowTextA("正在连接服务器......\r\n"); //提示用户正在连接服务器 if (::connect(s,(sockaddr *)&addr,sizeof(addr))) { GetDlgItem(IDC_EDIT3)->GetWindowTextA(str); str +="连接服务器成功!\r\n"; GetDlgItem(IDC_EDIT3)->SetWindowTextA(str); GetDlgItem(IDC_EDIT4)->EnableWindow(TRUE); GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE); GetDlgItem(IDC_EDIT1)->EnableWindow(FALSE); GetDlgItem(IDC_EDIT2)->EnableWindow(FALSE); } else { GetDlgItem(IDC_EDIT3)->GetWindowTextA(str); str += "连接服务器失败!请重试\r\n"; GetDlgItem(IDC_EDIT3)->SetWindowTextA(str); } } } void CSocket_ConnectDlg::OnClickedButton2() { // TODO: 在此添加控件通知处理程序代码 CString str,str1; GetDlgItem(IDC_EDIT4)->GetWindowTextA(str); if (str=="") { GetDlgItem(IDC_EDIT3)->GetWindowTextA(str1); str1 += "\r\n"; str1 += "消息不能为空\r\n"; GetDlgItem(IDC_EDIT3)->SetWindowTextA(str1); } else { ::send(s,str.GetBuffer(1),str.GetLength(),0); //发送消息到指定服务器 GetDlgItem(IDC_EDIT3)->GetWindowTextA(str1); str1 += "\r\n"; str1 += str; GetDlgItem(IDC_EDIT3)->SetWindowTextA(str1);//将发送内容输出到消息框 } } LRESULT CSocket_ConnectDlg::OnSocket(WPARAM wParam,LPARAM lParam) { char cs[100]=""; //定义数据缓冲区 if (lParam==FD_READ) //如果是套接字读取时间 { CString num=""; //定义字符串变量 recv(s,cs,100,NULL); //接受数据 GetDlgItem(IDC_EDIT3)->GetWindowText(num); num += "\r\n"; num += (LPTSTR)cs; //将接收到的数据转换为字符串 GetDlgItem(IDC_EDIT3)->SetWindowText(num);//设置输出消息内容 } return 0; }
5.代码下载地址:http://download.csdn.net/detail/u011028345/9869271