如何基于TCPIP协议进行MFC Socket网络通讯编程

    xiaoxiao2021-03-25  140

    MFC Socket网络通讯编程

           最近因为一个项目需要进行局域网络通讯,向工作单位的软件工程师请教了一下需要用到哪些知识,然后博主就自学了一遍windows网络通讯编程原理,然后就在网上找了一大堆例子,但实际运行效果并不佳,花了大概一周多的时间总算是把网络通讯程序给跑起来了(PS:虽然时间比较长,但对于一个新手程序员来说,算是不错的,哈哈。。。)

    声明:本程序是借鉴于此博文的源码(http://blog.csdn.net/lovey_carolin/article/details/6032195)。    

    TCP流式套接字的编程步骤:

    服务器端程序:

    1、加载套接字库  2、创建套接字(socket)。  3、将套接字绑定到一个本地地址和端口上(bind)。  4、将套接字设为监听模式,准备接收客户请求(listen)。  5、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。  6、用返回的套接字和客户端进行通信(send/recv)。  7、返回,等待另一客户请求。  8、关闭套接字。

    客户端程序:

    1、加载套接字库  2、创建套接字(socket)。  3、向服务器发出连接请求(connect)。  4、和服务器端进行通信(send/recv)。  5、关闭套接字。

    以上是Socket网络编程基本步骤,必须熟知!

    下面就讲讲如何在MFC中进行Socket 编程。

    首先新建一个基于对话框的工程取名为sFile,此工程作为服务器端。删除对话框中的中间代码和OK按钮。在新建的对话框中添加一个List Box控件作为显示窗口,并添加一个Edit control控件作为输入窗口,然后增加一个发送按钮:IDC_BtnSend。在List Box控件上右键点击选择——添加变量,将变量定义为控件类型并取名为m_listwords。

    然后再建一个客户端对话框取名为cFile,删除对话框中的中间代码和OK按钮。在新建的对话框中添加一个List Box控件作为显示窗口,并添加一个Edit control控件作为输入窗口,然后增加两个按钮:一个为发送按钮IDC_BtnSend,另一个为连接按钮IDC_BtnConnect。在List Box控件上右键点击选择——添加变量,将变量定义为控件类型并取名为m_listwords。另外需要添加一个IP Adress control控件,然后按添加变量的方法将其关联为控件类型,并取名为m_ip。

    服务器端具体步骤如下: 1、 在sFileDlg.h中添加public:void update(CString s);

                                           private: CEdit* send_edit;

           void CString2Char(CString str, char ch[]);  //此函数为字符格式转换函数,后面会讲到 2、 新建两个socket套接字: SOCKET listen_sock;                                                  SOCKET sock;

          在sFileDlg.h中添加 CString IP;  //定义为全局变量

          并声明线程函数  UINT server_thd(LPVOID p);

    3、在OnInitDialog()函数中添加:     

    send_edit = (CEdit *)GetDlgItem(IDC_EDIT1); send_edit->SetFocus(); char name[128]; hostent* pHost; gethostname(name, 128);//获得主机名  pHost = gethostbyname(name);//获得主机结构  IP = inet_ntoa(*(in_addr *)pHost->h_addr); update(_T("本服务器IP地址:") + IP); AfxBeginThread(server_thd, NULL);//创建线程 4、添加函数update():   void CSFileDlg::update(CString s) {  m_listwords.AddString(s); } 5、添加线程函数server_thd():   UINT server_thd(LPVOID p)//线程要调用的函数 { WSADATA wsaData; WORD wVersion; wVersion = MAKEWORD(2, 2); WSAStartup(wVersion, &wsaData); // WSAStartup(0x0202, &wsaData); SOCKADDR_IN local_addr; SOCKADDR_IN client_addr; int iaddrSize = sizeof(SOCKADDR_IN); int res; char msg[1024]; CsFileDlg * dlg = (CsFileDlg *)AfxGetApp()->GetMainWnd(); char ch_ip[20]; CString2Char(IP, ch_ip);//注意!这里调用了字符格式转换函数,此函数功能:CString类型转换为Char类型,实现代码在后面添加 //local_addr.sin_addr.s_addr = htonl(INADDR_ANY);//获取任意IP地址 local_addr.sin_addr.s_addr=inet_addr(ch_ip); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(8888); if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)//创建套接字 { dlg->update(_T("创建监听失败")); } if (bind(listen_sock, (struct sockaddr*) &local_addr, sizeof(SOCKADDR_IN)))//绑定套接字 { dlg->update(_T("绑定错误")); } listen(listen_sock, 1); if ((sock = accept(listen_sock, (struct sockaddr *)&client_addr, &iaddrSize)) == INVALID_SOCKET)//接收套接字 { dlg->update(_T("accept 失败")); } else { CString port; port.Format(_T("%d"), int(ntohs(client_addr.sin_port))); dlg->update(_T("已连接客户端:") + CString(inet_ntoa(client_addr.sin_addr)) + "  端口:" + port); } 接收数据 while (1) if ((res = recv(sock, msg, 1024, 0)) == -1) { dlg->update(_T("失去客户端的连接")); break; } else { msg[res] = '\0'; dlg->update(_T("client:") + CString(msg)); } } return 0; } 

    6、添加按钮发送函数(在对话框中右键点击刚添加的按钮弹出菜单,然后选择添加事件响应函数这一栏,将函数命名为OnSending)

         事件响应代码如下:

    void CsFileDlg::OnSending() { // TODO: Add your control notification handler code here CString s; char  msg[1024]; send_edit->GetWindowTextW(s); CString2Char(s, msg); //  注意!这里调用了字符格式转换函数,此函数功能:CString类型转换为Char类型,实现代码后面添加 if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR) { show_edit->ReplaceSel(_T("发送失败")); m_listwords.SetWindowTextW(_T("发送失败")); } else if (s == "") { MessageBox(_T("请输入信息")); } else { s = msg; //update(s);//消息上屏,清空输入,并重获焦点 //show_edit->ReplaceSel(_T("server:") + s);//消息上屏,清空输入,并重获焦点 m_listwords.AddString(_T("server:") + s); send_edit->SetWindowText(_T("")); m_listwords.SetFocus(); } }

    void CString2Char(CString str, char ch[])//此函数就是字符转换函数的实现代码 { int i; char *tmpch; int wLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);//得到Char的长度 tmpch = new char[wLen + 1];                                             //分配变量的地址大小 WideCharToMultiByte(CP_ACP, 0, str, -1, tmpch, wLen, NULL, NULL);       //将CString转换成char* for (i = 0; tmpch[i] != '\0'; i++) ch[i] = tmpch[i]; ch[i] = '\0'; }

    客户端具体步骤如下: 1、 在cFileDlg.h中添加 public:void update(CString s);

                                           private: CEdit* send_edit;

           void CString2Char(CString str, char ch[]);   2、 新建一个socket套接字: SOCKET sock;

          声明线程函数  UINT server_thd(LPVOID p);

    3、在OnInitDialog()函数中添加:     

    send_edit = (CEdit *)GetDlgItem(IDC_EDIT1); 4、添加函数update():   void CcFileDlg::update(CString s) {  m_listwords.AddString(s); }

    5、添加线程函数server_thd():

    UINT recv_thd(LPVOID p) { int res; char msg[1024]; //CString s; CcFileDlg * dlg = (CcFileDlg *)AfxGetApp()->GetMainWnd(); 接收数据 while (1) { if ((res = recv(sock, msg, 1024, 0)) == -1)//接收服务器的数据 { dlg->update(_T("失去连接")); break; } else { msg[res] = '\0'; dlg->update(_T("server:") + CString(msg)); } } //closesocket(sock); return 0; }

    6、添加连接按钮事件响应函数:在连接按钮上右键选择添加事件响应这一栏,将函数取名OnConnecting,如下

    void CcFileDlg::OnConnecting() { // TODO: Add your control notification handler code here WSADATA wsaData; SOCKADDR_IN server_addr; memset(&server_addr, 0, sizeof(server_addr)); WORD wVersion; wVersion = MAKEWORD(2, 2); WSAStartup(wVersion, &wsaData); // WSAStartup(0x0202, &wsaData); if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { update(_T("create socket error !!!")); } //CString ip; //ip_edit->GetWindowTextW(ip);//取得服务器的IP地址 //server_addr.sin_addr.s_addr = inet_addr((LPSTR)(LPCSTR)ip.GetBuffer()); BYTE nArrIP[4]; m_ip.GetAddress(nArrIP[0], nArrIP[1], nArrIP[2], nArrIP[3]); CString str; str.Format(_T("%d.%d.%d.%d"), nArrIP[0], nArrIP[1], nArrIP[2], nArrIP[3]); ip_edit->SetWindowTextW(str); char cp[50]; CString2Char(str, cp); server_addr.sin_addr.s_addr = inet_addr(cp); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8888); bind(sock, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)); if (connect(sock, (struct sockaddr *) &server_addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { update(_T("连接失败")); } else { //show_edit->SetWindowText(_T("")); update(_T("连接成功")); btnconn->EnableWindow(FALSE);//按钮变灰 AfxBeginThread(recv_thd, NULL); } }

    7、添加发送按钮事件响应函数:在发送按钮上右键选择添加事件响应这一栏,将函数取名OnSending,如下

    void CcFileDlg::OnSending() { // TODO: Add your control notification handler code here //CString s; //char * msg; //send_edit->GetWindowTextW(s); //msg = (LPSTR)(LPCTSTR)s; //CString2Char(s, msg); CString s; char msg[1024]; send_edit->GetWindowTextW(s); CString2Char(s, msg); if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR) { update(_T("发送失败")); } else if (s == "") { MessageBox(_T("请输入信息")); } else { s = msg; update(_T("client:") + s);//消息上屏,清空输入,并重获焦点 send_edit->SetWindowText(_T("")); send_edit->SetFocus(); } /*CString s; char * msg; send_edit->GetWindowText(s); msg = (char*)s.GetBuffer(s.GetLength()); if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR) { show_edit->ReplaceSel(_T("发送失败/r/n")); } else if (s == "") { MessageBox(_T("请输入信息")); } else { show_edit->ReplaceSel(_T("client:") + s + "/r/n");//消息上屏,清空输入,并重获焦点 send_edit->SetWindowText(_T("")); send_edit->SetFocus(); }*/ }

    8、最后添加字符格式转换函数 /* * 函数名: CString2Char * 参数1: CString str                 待转换字符串 * 参数2: char ch[]                       转换后将要储存的位置 * 将Unicode下的CString转换为char* */ void CString2Char(CString str, char ch[]) { int i; char *tmpch; int wLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);//得到Char的长度 tmpch = new char[wLen + 1];                                             //分配变量的地址大小 WideCharToMultiByte(CP_ACP, 0, str, -1, tmpch, wLen, NULL, NULL);       //将CString转换成char* for (i = 0; tmpch[i] != '\0'; i++) ch[i] = tmpch[i]; ch[i] = '\0'; }

    实验结果如图:

    转载请注明原文地址: https://ju.6miu.com/read-11455.html

    最新回复(0)