




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 Windows Socket 网络通信程序设计一、理论分析同步方式指的是发送方不等接收方响应,便接着发下个数据包的通信方式;而异步指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式。 阻塞套接字是指执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上,比如调用recv()函数读取网络缓冲区中的数据,如果没有数据到达,将一直挂在recv()这个函数调用上,直到读到一些数据,此函数调用才返回;而非阻塞套接字是指执行此套接字的网络调用时,不管是否执行成功,都立即返回。比如调用recv()函数
2、读取网络缓冲区中数据,不管是否读到数据都立即返回,而不会一直挂在此函数调用上。在实际Windows 网络通信软件开发中,异步非阻塞套接字是用的最多的。平常所说的C/S(客户端/服务器)结构的软件就是异步非阻塞模式的。 使用的是传统的socket (recv(),send() .)等。而且是阻塞模式,使用图形界面MFC编写 (VC 6.0)但为了能让程序不出现假死现象( recv,accept 这样的函数都会出现这样的事情),所以采用了多线程技术,其实也就是用了AfxBeginThread ,TerminateThread等等。这样对于阻塞函数都让他们在新建立的线程里运行
3、就好了。另外解决的一个大问题就是,创建的新线程无法对窗口进行操作,比如要自在编辑框显示一句话等等。如果直接取得窗口类的句柄操作,会出现wincore 的错误,也就是跨线程错误。主要原因也就是-MFC的线程被自己外部创建的线程调用就会有这个错误。解决的方法就是在线程里用SendMessage给窗口发送一个自定义的消息。比如我这里用的就是 要实现这样的方法在BEGIN_MESSAGE_MAP下面要添加ON_MESSAGE(WM_UpdateDATA, OnMyMessage) 绑定我自己的消息,这样外部的线程就可以通过SendMessage来调用窗体的函数OnMy
4、Message了,用来显示信息WM_UpdateDATA在stdafx.h中我自己定义为#define WM_UpdateDATA WM_USER+100 。定义一个自己的消息*/这样只要外部给窗口SendMessage -> WM_UpdateDATA 就会让窗口的onmymessage函数执行了,具体要显示的内容放在一个全局变量就好了。 serverdlg.cpp 二、程序代码/定义全局变量,方便各个工作线程和窗口线程的通信SOCKETsockuse,sock;int flag; /主要是用来标志是否连接,用来控制一些循环和功能HWND m
5、ydlg; /记录窗体的句柄HWND stopbutton; /记录关闭连接按钮的句柄 char buff200; /主要的缓冲区,显示数据使用,也供线程间通信使用 CWinThread* mainthread; /记录线程的句柄 UINT FileTrans(LPVOID pParam) /发送文件的线程函数 OPENFILENAMEA ofn; char szFile260; char path260; char filebuff100; int num; ZeroMemory(&ofn, sizeof(ofn); ofn.lStructS
6、ize = sizeof(ofn); ofn.hwndOwner = NULL; ofn.lpstrFile = szFile; ofn.lpstrFile0 = '0' ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = "所有文件*.*0" ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = 0; /以上的定义是为了建立一个文件打开对话框。
7、步骤/上来说都是固定的,这里只是借用一下 if (GetOpenFileNameA(&ofn)=FALSE) /判断文件路径取得是否正确 /若失败则恢复建立MainControl线程,进行数据接收处理。 mainthread=AfxBeginThread(MainControl,NULL); flag=1; /设定flag保证MainControl可以正常进行循环return 0; memset(filebuff,0,100); /清空filebuff strcpy(path,ofn.lpstrFile); /将路径拷贝到path中 strcpy(filebuff,"send
8、start"); send(sockuse,filebuff,strlen(filebuff),0); /发送请求信息 recv(sockuse,filebuff,100,0); /等待接收反馈信息 if(strcmp(filebuff,"sendagree") /若接收到的信息不是sendagree strcpy(buff,"对方接受错误"); SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息 mainthread=AfxBeginThread(MainControl,NULL
9、); /恢复建立MainControl线程 flag=1; /设定flag保证MainControl循环正常 return 0;SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息 while(!feof(fp) num=fread(filebuff,1,100,fp); /文件结束前每次读取100字节 send(sockuse,filebuff,num,0); /发送,最后一次不足100字节 /作为标志,可以让接受方知道文件结束 strcpy(buff,"发送完毕");SendMess
10、age(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息memset(buff,0,100);fclose(fp); /关闭文件 flag=1; return 0; UINT FileRecv() /接收文件函数 char sendbuff100; /发送缓冲区char recvbuff100; /接受缓冲区int num; /记录每次接受的字节数 CString szGetName; /记录保存的文件路径CFileDialog * lpszOpenFile; /定义一个CfileDialog对象
11、lpszOpenFile=new CFileDialog(false,"","",OFN_FILEMUSTEXIST|OFN_HIDEREADONLY,"文件类型(*.*)|*.*|");/生成一个对话框if(lpszOpenFile->DoModal()=IDOK)/假如点击对话框确定按钮szGetName = lpszOpenFile->GetPathName(); /得到打开文件的路径/SetWindowText(szGetName); /在窗口标题上显示路径delete lpszOpenFile; /释放分配的对话
12、框memset(sendbuff,0,100); strcpy(sendbuff,"sendagree"); send(sockuse,sendbuff,strlen(sendbuff),0); /发送sendagree告知对方开始发送吧 SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显/示信息 num=recv(sockuse,recvbuff,100,0); /接收数据FILE *fp=fopen(szGetName,"wb"); /打开文件,路径为szGe
13、tNamefwrite(recvbuff,num,1,fp); /写入之前的数据 num=recv(sockuse,recvbuff,100,0); fwrite(recvbuff,num,1,fp); fclose(fp); /结束后关闭文件。 strcpy(buff,"接收完毕");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,/显示信息return 0; int Addrlen=sizeof(sockaddr_in); /accept要用到的数值sockaddr_i
14、n ClientAddr; sockuse=accept(sock,(struct sockaddr FAR *)&ClientAddr,&Addrlen);/上一句的accept函数调用后会进行阻塞,造成未返回时程序假死,使用了单独的线程/就是为了防止这样的现象发生flag=1; /返回成功后,设定flag保证MainControl循环正常strcpy(buff,"已经连接!");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消/息,显示信息mainthread=AfxBeginThread(MainC
15、ontrol,NULL); /建立MainControl线程 return 0; char recvbuff100; /接受缓冲区memset(buff,0,100);while(flag)memset(recvbuff,0,100); /每次接收前清空缓冲区 recv(sockuse,recvbuff,100,0); /进行阻塞接收数据,如果不是用单独的线程/会造成程序假死,这也就是为什么我的程序使用单独的线程来处理 if(!strcmp(recvbuff,"end")SendMessag
16、e(stopbutton,BM_CLICK,NULL,NULL); /消息判断为end则调用/窗口的OnButtonEnd函数来结束连接的清理工作。return 0;else if(!strcmp(recvbuff,"sendstart")/接受的消息判断为"sendstart"则调用FileRecv() FileRecv(); else strcpy(buff,"client:");strcat(buff,recvbuff);SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给/窗口消息,
17、显示信息return 0; m_ctrlstop.EnableWindow(true); m_ctrlfile.EnableWindow(true);m_ctrlstart.EnableWindow(false);m_ctrlsend.EnableWindow(true); /以上使各个按钮进行使能 sockaddr_in ServerAddr; /开始建立socket WSADATA WSAData; if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0)showmess("SOCKET 初始化错误
18、rn");return; sock=socket(AF_INET,SOCK_STREAM,0); /采用流式套接字,ipv4if(sock=SOCKET_ERROR)showmess("SOCKET 创建错误!rn");WSACleanup(); return; ServerAddr.sin_family=AF_INET; ServerAddr.sin_addr.s_addr=htonl(INADDR_ANY); /任意ip作为本机ip ServerAddr.sin_port=htons(2006); /使用2006端口 if(bind(sock,(s
19、truct sockaddr FAR*)&ServerAddr,sizeof(ServerAddr)=SOCKET_ERROR) /绑定socket和本地地址showmess("绑定错误!n");return; /显示正在侦听 showmess("listening.");listen(sock,1);flag=0; /未连接之前,flag=0mydlg=this->GetSafeHwnd(); /取得窗口句柄供线程函数使用stopbutton=:GetDlgItem(mydlg,IDC_BUTT
20、ON_STOP); /取得关闭连接按/钮的句柄供线程函数使用 AfxBeginThread(WaitForAccept,NULL); /启动WaitForAccept线程等待连接 return; void CServerDlg:showmess(char *mess) /用来在信息窗口显示信息 m_strmess+=mess; m_strmess+="rn" /在每条信息后添加回车UpdateData(false); /更新信息显示 flag=0; /将
21、连接标志清零strcpy(buff,"end"); send(sockuse,buff,strlen(buff),0); /给对方发送信息告知结束TerminateThread(mainthread->m_hThread,0x01); /结束MainControl线程closesocket(sock); /关闭套接字closesocket(sockuse);WSACleanup(); /清理网络 m_ctrlstop.EnableWindow(false); /使能一些按钮m_ctrlfile.EnableWindow(false);m_ctrlstart.Enabl
22、eWindow(true); void CServerDlg:OnMyMessage() showmess(buff); /仅仅是为了线程函数调用内部的信息显示函数 void CServerDlg:OnButtonFile() flag=0; /设定 flag TerminateThread(mainthread->m_hThread,0x01); /中止MainControlAfxBeginThread(FileTrans,NULL); /启用FileTrans线程 void CServerDlg:OnButtonS
23、end() char talkbuff100;memset(talkbuff,0,100);UpdateData(true);strcpy(talkbuff,m_strtalk); /取得对话框数据send(sockuse,talkbuff,100,0); /发送给对方信息 clientDlg.cpp 首先是clientDlg.cpp #include "winsock.h" #include "stdio.h"#include"string.h"#pragm
24、a comment(lib,"wsock32.lib") /这四句要加在本文件的开头部分。保证/网络功能正常在BEGIN_MESSAGE_MAP下面要添加ON_MESSAGE(WM_UpdateDATA, OnMyMessage) /*绑定我自己的消息,这样外部的线程就可以通过SendMessage来调用窗体的函数OnMyMessage了,用来显示信息 WM_UpdateDATA在stdafx.h中我自己定义为#define WM_UpdateDATA WM_USER+100 /定义一个自己的消息*/ /定义全局变量,方便各个工
25、作线程和窗口线程的通信SOCKET sock;char buff100; /主要的缓冲区,显示数据使用,也供线程间通信使用HWND mydlg; /记录窗体的句柄sockaddr_in ServerAddr;int flag=0; /主要是用来标志是否连接,用来控制一些循环和功能CWinThread* mainthread;HWND stopbutton; /记录关闭连接按钮的句柄 UINT WaitForConnect(LPVOID pParam) /等待connect 的线程 if(connect(sock,(struct sockaddr*)&
26、amp;ServerAddr,sizeof(ServerAddr)=SOCKET_ERROR)strcpy(buff,"connect failn");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消/息,显示信息closesocket(sock); /失败则关闭sockreturn 0;strcpy(buff,"已经连接!");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL);/发送给窗口消/息,显示信息flag=1;mainthread=AfxBeginThre
27、ad(MainControl,NULL); /建立MainControl线程return 0; char sendbuff100; /发送缓冲区char recvbuff100; /接受缓冲区int num; /记录每次接受的字节数 CString szGetName;CFileDialog * lpszOpenFile; /定义一个CfileDialog对象lpszOpenFile = new CFileDialog(false,"","",OFN_FILEMUSTEXIST|OFN_HIDEREADONLY,"文件类型
28、(*.*)|*.*|");/生成一个对话框if(lpszOpenFile->DoModal()=IDOK)/假如点击对话框确定按钮szGetName = lpszOpenFile->GetPathName(); /得到打开文件的路径/SetWindowText(szGetName); /在窗口标题上显示路径delete lpszOpenFile; /释放分配的对话框 memset(sendbuff,0,100); strcpy(sendbuff,"sendagree"); send(sock,sendbuff,strlen(sendbuff)
29、,0); /发送sendagree告知对方开始发送吧 SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息 num=recv(sock,recvbuff,100,0); /接收数据FILE *fp=fopen(szGetName,"wb"); /打开文件,路径为szGetNamefwrite(recvbuff,num,1,fp); /写入之前的数据 while(num=100) /根据接收是否为100字节判断文件是否结束 num=recv(sock,recvbuff,100,0)
30、; fwrite(recvbuff,num,1,fp); strcpy(buff,"接收完毕");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息return 0; char recvbuff100;/接受缓冲区 memset(buff,0,100); while(flag) memset(recvbuff,0,100); /每次接收前清空缓冲区 recv(sock,recvbuff,100,0); /进行阻塞接收数据,如果不是用单独的线程/
31、会造成程序假死,这也就是为什么我的程序使用单独的线程来处理 if(!strcmp(recvbuff,"end") /消息判断为end则调用/窗口的OnButtonEnd函数来结束连接的清理工作。SendMessage(stopbutton,BM_CLICK,NULL,NULL); return 0;else if(!strcmp(recvbuff,"sendstart")/接受的消息判断为"sendstart"则调用FileRecv() FileRecv();memset(recvbuff,0,100); else strcpy(bu
32、ff,"server:");strcat(buff,recvbuff);SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息return 0; OPENFILENAMEA ofn; char szFile260; char path260; char filebuff100; int num; ZeroMemory(&ofn, sizeof(ofn); ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = NULL; ofn.lpstrFile = sz
33、File; ofn.lpstrFile0 = '0' ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = "所有文件*.*0" ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = 0; /以上的定义是为了建立一个文件打开对话框。步骤/上来说都是固定的,这里只是借用一下 if (GetOpenFileNameA(&ofn)=
34、FALSE) /判断文件路径取得是否正确 /若失败则恢复建立MainControl线程,进行数据接收处理。 mainthread=AfxBeginThread(MainControl,NULL); flag=1; /设定flag保证MainControl可以正常进行循环return 0; memset(filebuff,0,100); /清空filebuff strcpy(path,ofn.lpstrFile); /将路径拷贝到path中 strcpy(filebuff,"sendstart"); send(sock,filebuff,strlen(filebu
35、ff),0); /发送请求信息 recv(sock,filebuff,100,0); /等待接收反馈信息 if(strcmp(filebuff,"sendagree") /若接收到的信息不是sendagree strcpy(buff,"对方接受错误"); SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息 mainthread=AfxBeginThread(MainControl,NULL); /恢复建立MainControl线程 flag=1; /设定flag保证MainControl循环
36、正常 return 0; strcpy(buff,"对方已经同意,开始发送文件"); SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息 FILE *fp=fopen(path,"rb"); /打开文件 while(!feof(fp) num=fread(filebuff,1,100,fp); /文件结束前每次读取100字节 send(sock,filebuff,num,0); /发送,最后一次不足100字节 /
37、作为标志,可以让接受方知道文件结束 strcpy(buff,"发送完毕");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /发送给窗口消息,显示信息memset(buff,0,100);fclose(fp); /关闭文件 mainthread=AfxBeginThread(MainControl,NULL); /恢复建立MainControl线程 flag=1; return 0; m_ctrlstop.EnableWindow(true);m_ctrlfile.EnableWindow(true);m_ctrlconn
38、ect.EnableWindow(false);/以上使各个按钮进行使能 WSADATA WSAData;/开始建立socket if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0)showmess("socket初始化错误");return; sock=socket(AF_INET,SOCK_STREAM,0);/采用流式套接字,ipv4 if(sock=SOCKET_ERROR)showmess("SOCK Create FAIL!");WSACleanup
39、(); return;ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(2006);/使用2006端口UpdateData(true); /读取ip 值ServerAddr.sin_addr.s_addr = inet_addr(m_strip);/使用编辑框中的ip地址,默认值 /取得窗口句柄供线程函数使用flag=0;stopbutton=:GetDlgItem(mydlg,IDC_BUTTON_STOP);/取得关闭连接按钮的句柄供线程函数使用AfxBeginThread(WaitForConn
40、ect,NULL);/启动WaitForConnect线程进行连接return; m_strmess+=mess;m_strmess+="rn" /在每条信息后添加回车UpdateData(false); /更新信息显示 void CClientDlg:OnMyMessage() showmess(buff);/仅仅是为了线程函数调用内部的信息显示函数 TerminateThread(mainthread,0x01);/结束MainControl线程strcpy(buff,"end");send(sock,buff,strlen(buff),0);/给对方发送信息告知结束closesocket(sock);/关闭套接字 WSACleanup();m_ctrlstop.EnableWindow(false);/使一些按钮m_ctrlfile.EnableWindow(false);m_ctrlconnect.EnableWindow(true);
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度实习生实习期间人身安全及保险责任协议
- 必考人力资源管理师试题及答案
- 2025年度煤炭产业政策支持合同担保书范本
- 2025年度电力工程设计与施工监理电力工程设计合同
- 2025年黄金饰品品牌授权与区域市场开发合同
- 2025年度自建房施工安全责任合同协议
- 二零二五年度汽车按揭贷款合同修改合同样本
- 2025年度矿山员工劳动合同与矿山救援队伍培训协议
- 二零二五年度智能设备销售总额提成管理服务合同
- 2025年茶艺师学习计划试题及答案
- 人教版新起点英语四年级下册Unit 2《Cities》单元教学目标
- 年产5000吨午餐肉罐头工厂设计-毕业论文
- GB/T 7777-2021容积式压缩机机械振动测量与评价
- 中国舞蹈家协会七级教材讲课教案
- GB/T 21224-2007评定绝缘材料水树枝化的试验方法
- GB 15322.2-2019可燃气体探测器第2部分:家用可燃气体探测器
- 饭店管理概论全套课件
- shiy实验:探究小车速度速度随时间变化的规律
- 钢筋加工棚搭设方案
- 六年级下册美术第10课头饰和帽子苏少版课件
- 来料检验标准书模板
评论
0/150
提交评论