1 服务器端
由先得专门为服务器端做一个Socket通信类CNewSocket类,此类继承CAsyncSocket类,专门负责服务器端socket通信事情:
NewSocket.h:
#pragma once #include "afxsock.h" //此类专门用来与客户端进行socket通信 class CNewSocket : public CAsyncSocket { public: CNewSocket(void); ~CNewSocket(void); virtual void OnReceive(int nErrorCode); virtual void OnSend(int nErrorCode); // 消息长度 UINT m_nLength; //消息缓冲区 char m_szBuffer[4096]; };
对应实现代码如下:
NewSocket.cpp:
#include "StdAfx.h" #include "NewSocket.h" CNewSocket::CNewSocket(void) : m_nLength(0) { memset(m_szBuffer,0,sizeof(m_szBuffer)); } CNewSocket::~CNewSocket(void) { if(m_hSocket !=INVALID_SOCKET) { Close(); } } void CNewSocket::OnReceive(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class m_nLength =Receive(m_szBuffer,sizeof(m_szBuffer),0); //接收数据 m_szBuffer[m_nLength] ='\0'; //接收消息后就开始给客户端发相同的消息 AsyncSelect(FD_WRITE); //触发OnSend函数 CAsyncSocket::OnReceive(nErrorCode); } void CNewSocket::OnSend(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class char m_sendBuf[4096]; //消息缓冲区 strcpy(m_sendBuf,"server send:"); strcat(m_sendBuf,m_szBuffer); Send(m_sendBuf,strlen(m_sendBuf)); //触发OnReceive函数 AsyncSelect(FD_READ); CAsyncSocket::OnSend(nErrorCode); }
如上,NewSocket类重载了CAsyncSocket类的接收与发送事件处理例程,一旦被触发了发送或接收事件,将调用此对应函数.
接下来服务器端在做一个专门类CListenSocket类,用来监听来自客户端的连接请求,如下:
ListenSocket.h:
#pragma once #include "afxsock.h" #include "NewSocket.h" class CListenSocket : public CAsyncSocket { public: CListenSocket(void); ~CListenSocket(void); CNewSocket *m_pSocket; //指向一个连接的socket对象 virtual void OnAccept(int nErrorCode); };由上可知,CListenSocket类还是继承了CAsyncSocket类,并重载了其接受事件,且包含了一个由之前定义好的CNewSocket类指针做为成员,用来指向与客户端连接好的连接.
其实现代码如下:
ListenSocket.cpp:
#include "StdAfx.h" #include "ListenSocket.h" CListenSocket::CListenSocket(void) { } CListenSocket::~CListenSocket(void) { } void CListenSocket::OnAccept(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class CNewSocket *pSocket =new CNewSocket(); if(Accept(*pSocket)) { pSocket->AsyncSelect(FD_READ); //触发通信socket的Read函数读数据 m_pSocket =pSocket; } else { delete pSocket; } CAsyncSocket::OnAccept(nErrorCode); }
这个OnAccept事件触发是在Listen之后,如果有客户端尝试连接时触发,且先看后续内容.
接下来就是对话框的代码了:
SocketServerDlg.h:
// SocketServerDlg.h : header file // #pragma once #include "ListenSocket.h" // CSocketServerDlg dialog class CSocketServerDlg : public CDialogEx { // Construction public: CSocketServerDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data enum { IDD = IDD_SOCKETSERVER_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support CListenSocket m_srvrSocket; //监听套接字 // Implementation protected: HICON m_hIcon; // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedBtStart(); };在这个CSocketServerDlg类的声明内包含了一个CListenSocket成员m_srvrSocket,并有一个start按键.
// SocketServerDlg.cpp : implementation file // #include "stdafx.h" #include "SocketServer.h" #include "SocketServerDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CAboutDlg dialog used for App About class CAboutDlg : public CDialogEx { public: CAboutDlg(); // Dialog Data enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CSocketServerDlg dialog CSocketServerDlg::CSocketServerDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CSocketServerDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CSocketServerDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CSocketServerDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BT_START, &CSocketServerDlg::OnBnClickedBtStart) END_MESSAGE_MAP() // CSocketServerDlg message handlers BOOL CSocketServerDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. 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); } } // 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 return TRUE; // return TRUE unless you set the focus to a control } void CSocketServerDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CSocketServerDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CSocketServerDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CSocketServerDlg::OnBnClickedBtStart() { // TODO: Add your control notification handler code here if(m_srvrSocket.m_hSocket ==INVALID_SOCKET) { //创建监听套接字,激发FD_ACCEPT事件,默认端口7190 BOOL bFlag =m_srvrSocket.Create(7190,SOCK_STREAM,FD_ACCEPT); //第三个参数表示m_srvrSocket只对FD_ACCEPT事件感兴趣 if(!bFlag) { AfxMessageBox(_T("Socket创建失败!")); m_srvrSocket.Close(); PostQuitMessage(0); return; } GetDlgItem(IDC_BT_START)->EnableWindow(FALSE); //监听成功,等待连接请求 if(!m_srvrSocket.Listen())//如果监听失败 { int nErrorCode =m_srvrSocket.GetLastError(); //检测错误信息 if(nErrorCode !=WSAEWOULDBLOCK) //如果不是线程阻塞 { AfxMessageBox(_T("Socket错误!")); m_srvrSocket.Close(); PostQuitMessage(0); return; } } } }
当用户按下start按键时,程序首先调用m_srvrSocket的Create函数创建一个socket,然后调用它的Listen函数监听客户端的连接请求,一旦客户端尝试连接服务器,那将触发m_srvrSocket的FD_ACCEPT事件.接着在m_srvrSocket的OnAccept函数内,将创建一个CNewSocket对象,并利用此对象来操作socket收发,到此,服务器端的代码完.
2 客户端
这里的客户端的功能就是与服务器建立连接,交用户的数据发送给服务器,并显示来自服务器的接收数据.
与服务器类似,首先做一个专门用于socket通信的类ClientSocket类:
ClientSocket.h:
#pragma once #include "afxsock.h" class ClientSocket : public CAsyncSocket { public: ClientSocket(void); ~ClientSocket(void); // 是否连接 bool m_bConnected; // 消息长度 UINT m_nLength; //消息缓冲区 char m_szBuffer[5096]; virtual void OnConnect(int nErrorCode); virtual void OnReceive(int nErrorCode); virtual void OnSend(int nErrorCode); };由上类声明可知,此类重载了CAsyncSocket的连接,接收,发送事件例程.其实现代码如下:
#include "StdAfx.h" #include "ClientSocket.h" #include "SocketTest.h" #include "SocketTestDlg.h" ClientSocket::ClientSocket(void) : m_bConnected(false) , m_nLength(0) { memset(m_szBuffer,0,sizeof(m_szBuffer)); } ClientSocket::~ClientSocket(void) { if(m_hSocket !=INVALID_SOCKET) { Close(); } } void ClientSocket::OnConnect(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class //连接成功 if(nErrorCode ==0) { m_bConnected =TRUE; //获取主程序句柄 CSocketTestApp *pApp =(CSocketTestApp *)AfxGetApp(); //获取主窗口 CSocketTestDlg *pDlg =(CSocketTestDlg *)pApp->m_pMainWnd; //在主窗口输出区显示结果 CString strTextOut; strTextOut.Format(_T("already connect to ")); strTextOut +=pDlg->m_Address; strTextOut += _T(" 端口号:"); CString str; str.Format(_T("%d"),pDlg->m_Port); strTextOut +=str; pDlg->m_MsgR.InsertString(0,strTextOut); //激活一个网络读取事件,准备接收 AsyncSelect(FD_READ); } CAsyncSocket::OnConnect(nErrorCode); } void ClientSocket::OnReceive(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class //获取socket数据 m_nLength =Receive(m_szBuffer,sizeof(m_szBuffer)); //获取主程序句柄 CSocketTestApp *pApp =(CSocketTestApp *)AfxGetApp(); //获取主窗口 CSocketTestDlg *pDlg =(CSocketTestDlg *)pApp->m_pMainWnd; CString strTextOut(m_szBuffer); //在主窗口的显示区显示接收到的socket数据 pDlg->m_MsgR.InsertString(0,strTextOut); memset(m_szBuffer,0,sizeof(m_szBuffer)); CAsyncSocket::OnReceive(nErrorCode); } void ClientSocket::OnSend(int nErrorCode) { // TODO: Add your specialized code here and/or call the base class //发送数据 Send(m_szBuffer,m_nLength,0); m_nLength =0; memset(m_szBuffer,0,sizeof(m_szBuffer)); //继续提请一个读的网络事件,接收socket消息 AsyncSelect(FD_READ); CAsyncSocket::OnSend(nErrorCode); }
接下来就是对话框代码了:
SocketTestDlg.h:
// SocketTestDlg.h : header file // #pragma once #include "afxwin.h" #include "ClientSocket.h" // CSocketTestDlg dialog class CSocketTestDlg : public CDialogEx { // Construction public: CSocketTestDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data enum { IDD = IDD_SOCKETTEST_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: HICON m_hIcon; // Generated message map functions virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: // 服务器IP地址 CString m_Address; // 服务器端口号 int m_Port; // 消息接收显示控件 CListBox m_MsgR; afx_msg void OnBnClickedBtClear(); afx_msg void OnBnClickedOk(); // 用户输入的即将发送的内容 CString m_MsgS; afx_msg void OnBnClickedBtConnect(); afx_msg void OnTimer(UINT_PTR nIDEvent); // 连接服务器次数 int m_nTryTimes; ClientSocket m_ClientSocket; afx_msg void OnBnClickedBtSend(); afx_msg void OnBnClickedBtClose(); };
其实现代码如下:
// SocketTestDlg.cpp : implementation file // #include "stdafx.h" #include "SocketTest.h" #include "SocketTestDlg.h" #include "afxdialogex.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CAboutDlg dialog used for App About class CAboutDlg : public CDialogEx { public: CAboutDlg(); // Dialog Data enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CSocketTestDlg dialog CSocketTestDlg::CSocketTestDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CSocketTestDlg::IDD, pParent) , m_Address(_T("127.0.0.1")) , m_Port(0) , m_MsgS(_T("")) , m_nTryTimes(0) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CSocketTestDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_ET_IPADDRESS, m_Address); DDX_Text(pDX, IDC_ET_PORT, m_Port); DDX_Control(pDX, IDC_LIST_R, m_MsgR); DDX_Text(pDX, IDC_ET_SEND, m_MsgS); } BEGIN_MESSAGE_MAP(CSocketTestDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BT_CLEAR, &CSocketTestDlg::OnBnClickedBtClear) ON_BN_CLICKED(IDOK, &CSocketTestDlg::OnBnClickedOk) ON_BN_CLICKED(IDC_BT_CONNECT, &CSocketTestDlg::OnBnClickedBtConnect) ON_WM_TIMER() ON_BN_CLICKED(IDC_BT_SEND, &CSocketTestDlg::OnBnClickedBtSend) ON_BN_CLICKED(IDC_BT_CLOSE, &CSocketTestDlg::OnBnClickedBtClose) END_MESSAGE_MAP() // CSocketTestDlg message handlers BOOL CSocketTestDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. 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); } } // 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 return TRUE; // return TRUE unless you set the focus to a control } void CSocketTestDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CSocketTestDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } // The system calls this function to obtain the cursor to display while the user drags // the minimized window. HCURSOR CSocketTestDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CSocketTestDlg::OnBnClickedBtClear() { // TODO: Add your control notification handler code here m_MsgR.ResetContent(); } void CSocketTestDlg::OnBnClickedOk() { // TODO: Add your control notification handler code here /*CDialogEx::OnOK();*/ UpdateData(TRUE); if(m_MsgS.IsEmpty()) { AfxMessageBox(_T("please type the message you want to send!")); return; } } void CSocketTestDlg::OnBnClickedBtConnect() { // TODO: Add your control notification handler code here //如果当前已经与服务器建立了连接,则直接返回 if(m_ClientSocket.m_bConnected) { AfxMessageBox(_T("当前已经与服务器建立连接")); return; } UpdateData(TRUE); if(m_Address.IsEmpty()) { AfxMessageBox(_T("服务器的IP地址不能为空!")); return; } if(m_Port <=1024) { AfxMessageBox(_T("服务器的端口设置非法!")); return; } //使Connect按键失能 GetDlgItem(IDC_BT_CONNECT)->EnableWindow(FALSE); //启动连接定时器,每1秒中尝试一次连接 SetTimer(1,1000,NULL); } void CSocketTestDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: Add your message handler code here and/or call default if(m_ClientSocket.m_hSocket ==INVALID_SOCKET) { BOOL bFlag =m_ClientSocket.Create(0,SOCK_STREAM,FD_CONNECT); //创建套接字 if(!bFlag) { AfxMessageBox(_T("Socket创建失败!")); m_ClientSocket.Close(); PostQuitMessage(0);//退出 return; } } m_ClientSocket.Connect(m_Address,m_Port); //连接服务器 if(m_nTryTimes >=10) { KillTimer(1); AfxMessageBox(_T("连接失败!")); GetDlgItem(IDC_BT_CONNECT)->EnableWindow(TRUE); return; } else if(m_ClientSocket.m_bConnected) { KillTimer(1); GetDlgItem(IDC_BT_CONNECT)->EnableWindow(TRUE); return; } CString strTextOut =_T("尝试连接服务器第"); m_nTryTimes ++; CString str; str.Format(_T("%d"),m_nTryTimes); strTextOut +=str; strTextOut +=_T("次..."); m_MsgR.InsertString(0,strTextOut); CDialogEx::OnTimer(nIDEvent); } void CSocketTestDlg::OnBnClickedBtSend() { // TODO: Add your control notification handler code here UpdateData(TRUE); if(m_ClientSocket.m_bConnected) { //将用户输入的数据从CString转化为char *字符串,即Unicode-->ANSI,并保存到m_ClientSocket.m_szBuffer中 int len =WideCharToMultiByte(CP_ACP,0,m_MsgS,-1,NULL,0,NULL,NULL); WideCharToMultiByte(CP_ACP,0,m_MsgS,-1,m_ClientSocket.m_szBuffer,len,NULL,NULL ); m_ClientSocket.m_nLength =strlen(m_ClientSocket.m_szBuffer); m_ClientSocket.AsyncSelect(FD_WRITE);//触发写事件 m_MsgS =_T(""); UpdateData(FALSE); } } void CSocketTestDlg::OnBnClickedBtClose() { // TODO: Add your control notification handler code here m_ClientSocket.ShutDown(); EndDialog(0); }
OK,完!
作者:flydream0 发表于2013-1-26 20:37:26 原文链接
阅读:0 评论:0 查看评论