《网络程序设计》课程设计报告书题目:局域网文件传输与消息通信专业:网络工程学号:20091423学生姓名:史政法指导教师:魏宁完成日期:2012年06月06日一、题目及要求Window程序设计的基本要求:1.熟练掌握网络的基本概念和原理;2.熟练掌握网络编程接口Socket概念及编程原理;3.掌握基于TCP/IP的Internet编程技术;4.掌握各种软件开发工具的使用过程及方法。
本实例程序的技术要点是:●如何从CasyncSocket类派生出自己的WinSock类。
●理解WinSock类与应用程序框架的关系。
●重点学习流式套接字对象的使用。
●处理网络事件的方法。
二、系统概要设计1、CSocket类的介绍在MFC中,为了提供面向对象的编程方式而封装了两个WinSock类:一个是CAsyncSocket类,另一个是CSocket类。
他们的不同点在于前者使用的是非阻塞模式操作,而后者可以支持阻塞模式的I/O操作。
CSocket类是从CAsyncSocket类中继承来的,他为程序员提供了更高级的抽象性。
由于CSocket类和CArchive类一起使用,并且管理了通讯的大多数操作,从而使程序员从网络编程的底层细节中解脱了出来,不必使用CAsyncSocket 的一些成员函数和一些Windows Socket API函数来处理通信问题。
共同使用CSocket和CArchive类对象进行编程是最简单的WinSock编程模式。
下面是使用CSocket类进行网络通信的过程(1)构造CSocket类对象(2)用CSocket类对象的Create函数创建Windows Socket,Create()函数会调用Bind()函数将此函数绑定的指定的地址和端口。
(3)Socket创建完毕后,Listen()函数在服务器端进行监听客户的连接请求。
(4)在这一步,服务器端调用Accept()函数对客户端发来的请求进行接受和处理,而客户端则调用Connect()函数向服务器发出连接请求。
(5)在客户端和服务器端的数据传输完成后,应该分别在两边销毁创建的CSocket 对象。
2、编程思路和工程实现用MFC AppWizard新建一个给予对话框的工程,在第三步中添加Windows SocketS的支持,建立的程序界面如右图:图1—-程序主对话框中的控件属性控件类型控件IDCaption 静态文本 static text IDC_STATIC本机的ip : 静态文本 static text IDC_STATIC_SERVPOR T 要连接的ip : 静态文本 static text IDC_STATIC_MSG 消 息 IP 地址 Internet add IDC_MYIP IP 地址 Internet add IDC_YOURIP 编辑框 edit box IDC_EDIT_MSG 命令按钮 button IDC_BUTTONCONNECT 启动 命令按钮 button IDC_BUTTON_DISCONNE 关闭 命令按钮 button IDC_BUTTON_SENDMSG 发送消息 命令按钮 button IDC_BUTTON_SEND_FIL 传送文件 命令按钮 button IDC_BUTTON_STOP 停止传送 命令按钮 button IDCANCEL退出系统 进程 processIDC_PROGRESS_SEND_F图2—-对话框中的控件对象定义相应的成员变量编程思路:首先在对话框中输入要连接的IP地址,然后单击“启动”按钮,要连接的主机也做相同的操作,两边都准备完事后,两台计算机就可以消息和文件的传输了。
单击“启动”按钮时,处理函数启动了监听线程来启动服务,主要的语句如下:pThreadListen = ::AfxBeginThread(_ListenTcpThread,this);点击“发送消息”按钮时,处理函数启动了消息发送线程来进行消息的传送,语句如下:pThreadSendMsg = ::AfxBeginThread(_SendMsgThread,this);当点击“传送文件”按钮时,处理函数启动了文件传输线程来进行文件传输,语句如下:pThreadSendMsg = ::AfxBeginThread(_SendFileThread,this);下面主要介绍一下要用到的三个线程和一个文件接收处理函数。
首先要产生三个线程对象:CWinThread _ListenTcpThread;CWinThread _SendFileThread;CWinThread _SendMsgThread;三、系统详细设计1.监听线程函数UINT _ListenTcpThread(LPVOID lparam){CFiletranDlg *pDlg=(CFiletranDlg *)lparam;if(pDlg->StopServer==true) return -1;CSocket sockSrvr;pDlg->m_Potr=PORT;//保存当前使用端口,用于关闭int createSucceed=sockSrvr.Create(pDlg->m_Potr);if(createSucceed==0){AfxMessageBox("_ListenTcpThread Create错误!"+pDlg->GetError(GetLastError()));return -1;}int listenSucceed=sockSrvr.Listen(); //开始监听if(listenSucceed==0){AfxMessageBox("_ListenTcpThread Listen错误!"+pDlg->GetError(GetLastError()));return -1;}CSocket recSo;SOCKADDR_IN client;int iAddrSize=sizeof(client);int acceptSucceed=sockSrvr.Accept(recSo,(SOCKADDR *)&client,&iAddrSize); //接受连接并取得对方IPif(acceptSucceed==0){AfxMessageBox("_ListenTcpThread Accept错误!"+pDlg->GetError(GetLastError()));return -1;}sockSrvr.Close();//关闭char flag[FLAG]={0};if(recSo.Receive(flag,FLAG)!=2){return -1;}pDlg->m_type=flag[0];if(pDlg->m_type=='D') return 0;pThreadListen=::AfxBeginThread(_ListenTcpThread,pDlg);pDlg->ReceiveFileMsg(recSo,client);return 0;}2.传送文件线程函数UINT _SendFileThread(LPVOID lparam){CFiletranDlg *pDlg=(CFiletranDlg *)lparam;if(pDlg->StopServer==true) return -1;CSocket sockClient;sockClient.Create();CString ip;pDlg->m_YourIp.GetWindowText(ip);sockClient.Connect(ip, PORT);//首先发送标记F为文件,2int end=0;end=sockClient.Send("F",FLAG);///////////////////////////////////////////////////////////////////发送标志是否成功if(end==SOCKET_ERROR){AfxMessageBox("_SendFileThread Send错误!"+pDlg->GetError(GetLastError()));return -1;}else if(end!=2){AfxMessageBox("文件头错误");return -1;}///////////////////////////////////////////////////////////////////CFile myFile;FILEINFO myFileInfo;if(!myFile.Open(pDlg->m_fileName, CFile::modeRead | CFile::typeBinary)) return -1;myFileInfo.fileLength=myFile.GetLength(); //得到文件大小strcpy(myFileInfo.fileName,myFile.GetFileName());//得到文件名称sockClient.Send(&myFileInfo,sizeof(myFileInfo)); //发送文件信息pDlg->m_Progress.SetRange32(0,myFileInfo.fileLength);myFile.Seek(0,CFile::begin);char m_buf[SIZEFILE]={0};CString strError;int num=0;end=0;int temp=0;for(;;){if(pDlg->FileWork==false){pDlg->FileWork=true;pDlg->GetDlgItem(IDCANCEL)->EnableWindow(false);pDlg->GetDlgItem(IDC_BUTTON_DISCONNECT)->EnableWindow(false);}num=myFile.Read(m_buf, SIZEFILE);if(num==0) break;end=sockClient.Send(m_buf, num);temp+=end;pDlg->m_Progress.SetPos(temp);if(pDlg->FileStop==true){pDlg->FileStop=false;pDlg->FileWork=false;break;}if(end==SOCKET_ERROR){AfxMessageBox("_SendFileThread Send错误!"+pDlg->GetError(GetLastError()));break;}}pDlg->m_Progress.SetPos(0);CString strLocalName;pDlg->GetLocalHostName(strLocalName);CString strLocalIP;pDlg->GetIpAddress(strLocalName,strLocalIP);if(temp==myFileInfo.fileLength)AfxMessageBox("文件发送成功");elseAfxMessageBox("文件发送失败");myFile.Close();sockClient.Close();pDlg->FileWork=false;pDlg->GetDlgItem(IDC_PROGRESS_SEND_FILE)->ShowWindow(SW_HIDE);pDlg->GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(false);pDlg->GetDlgItem(IDCANCEL)->EnableWindow(true);pDlg->GetDlgItem(IDC_BUTTON_DISCONNECT)->EnableWindow(true);return 0;}3.发送消息线程函数UINT _SendMsgThread(LPVOID lparam) //TCP发送信息线程{CFiletranDlg *pDlg=(CFiletranDlg *)lparam;if(pDlg->StopServer==true) return -1;CSocket sockClient;sockClient.Create();CString ip,strError;pDlg->m_YourIp.GetWindowText(ip);int conn=sockClient.Connect(ip, PORT);if(conn==0) ///////////////////////////////////{AfxMessageBox("_SendMsgThread Connect错误!"+pDlg->GetError(GetLastError()));sockClient.ShutDown(2);sockClient.Close();AfxEndThread(1L);return 0;}//首先发送标记M为信息,2int end=0;end=sockClient.Send("M",FLAG);if(end==SOCKET_ERROR){AfxMessageBox("_SendMsgThread Send错误!"+pDlg->GetError(GetLastError()));return -1;}else if(end!=2){AfxMessageBox("消息头错误");return -1;}CString strMsg=pDlg->message;end=sockClient.Send(strMsg,strMsg.GetLength());if(end==SOCKET_ERROR){AfxMessageBox("_SendMsgThread Send错误!"+pDlg->GetError(GetLastError()));return -1;}int i=0;sockClient.Close();return 0;}4.文件接收处理函数int CFiletranDlg::SaveYourFile(CSocket &recSo, SOCKADDR_IN &client){CString fname;CFileDialog dlg(false); //另存文件FILEINFO myFileInfo;recSo.Receive(&myFileInfo,sizeof(FILEINFO));int fileLength=myFileInfo.fileLength;CString strfileIp,strfileName,strfileLength;strfileIp.Format(inet_ntoa(client.sin_addr));strfileName.Format(myFileInfo.fileName);strfileLength.Format("%f",myFileInfo.fileLength/1024.0);CString title="文件"+strfileName+" 大小"+strfileLength+"KB "+"来在"+strfileIp+" 是否接受";dlg.m_ofn.lpstrTitle=title;//标题条char fileme[500]={0};//必须足够大小strcpy(fileme,strfileIp+strfileName);dlg.m_ofn.lpstrFile=fileme; //文件名称if(dlg.DoModal()==IDOK){fname=dlg.GetPathName(); //得到文件名名称、路径GetDlgItem(IDC_PROGRESS_SEND_FILE)->ShowWindow(SW_SHOW);}else{GetDlgItem(IDC_PROGRESS_SEND_FILE)->ShowWindow(SW_HIDE);GetDlgItem(IDC_BUTTON_DISCONNECT)->EnableWindow(true);GetDlgItem(IDCANCEL)->EnableWindow(true);recSo.Close();return 0;}char buf[SIZEFILE]={0};CFile f(fname,CFile::modeCreate|CFile::modeWrite); //存文件m_Progress.SetRange32(0,fileLength);int n=0; //接受的字节数0表示结束int temp=0;GetDlgItem(IDCANCEL)->EnableWindow(false);GetDlgItem(IDC_BUTTON_DISCONNECT)->EnableWindow(false);for(;;){n=recSo.Receive(buf,SIZEFILE); //接受if(n==0) //0表示结束break; //接受完毕f.Write(buf,n);temp+=n;m_Progress.SetPos(temp);if(FileWork==false) FileWork=true;if(FileStop==true){FileStop=false;FileWork=false;break ;}}f.Close();m_Progress.SetPos(0);if(temp==fileLength)MessageBox("文件接受成功");elseMessageBox("文件接受失败");FileWork=false;GetDlgItem(IDC_PROGRESS_SEND_FILE)->ShowWindow(SW_HIDE);GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(false);GetDlgItem(IDCANCEL)->EnableWindow(true);GetDlgItem(IDC_BUTTON_DISCONNECT)->EnableWindow(true);return 0;}四、课程设计总结采用多线程方法,利用MFC的CSocket类,可以简单方便的实现计算机用户间的文件传输和消息发送功能。