




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、 Windows Socket 網(wǎng)絡(luò)通信程序設(shè)計一、理論分析同步方式指的是發(fā)送方不等接收方響應(yīng),便接著發(fā)下個數(shù)據(jù)包的通信方式;而異步指發(fā)送方發(fā)出數(shù)據(jù)后,等收到接收方發(fā)回的響應(yīng),才發(fā)下一個數(shù)據(jù)包的通信方式。 阻塞套接字是指執(zhí)行此套接字的網(wǎng)絡(luò)調(diào)用時,直到成功才返回,否則一直阻塞在此網(wǎng)絡(luò)調(diào)用上,比如調(diào)用recv()函數(shù)讀取網(wǎng)絡(luò)緩沖區(qū)中的數(shù)據(jù),如果沒有數(shù)據(jù)到達,將一直掛在recv()這個函數(shù)調(diào)用上,直到讀到一些數(shù)據(jù),此函數(shù)調(diào)用才返回;而非阻塞套接字是指執(zhí)行此套接字的網(wǎng)絡(luò)調(diào)用時,不管是否執(zhí)行成功,都立即返回。比如調(diào)用recv()函數(shù)
2、讀取網(wǎng)絡(luò)緩沖區(qū)中數(shù)據(jù),不管是否讀到數(shù)據(jù)都立即返回,而不會一直掛在此函數(shù)調(diào)用上。在實際Windows 網(wǎng)絡(luò)通信軟件開發(fā)中,異步非阻塞套接字是用的最多的。平常所說的C/S(客戶端/服務(wù)器)結(jié)構(gòu)的軟件就是異步非阻塞模式的。 使用的是傳統(tǒng)的socket (recv(),send() .)等。而且是阻塞模式,使用圖形界面MFC編寫 (VC 6.0)但為了能讓程序不出現(xiàn)假死現(xiàn)象( recv,accept 這樣的函數(shù)都會出現(xiàn)這樣的事情),所以采用了多線程技術(shù),其實也就是用了AfxBeginThread ,TerminateThread等等。這樣對于阻塞函數(shù)都讓他們在新建立的線程里運行
3、就好了。另外解決的一個大問題就是,創(chuàng)建的新線程無法對窗口進行操作,比如要自在編輯框顯示一句話等等。如果直接取得窗口類的句柄操作,會出現(xiàn)wincore 的錯誤,也就是跨線程錯誤。主要原因也就是-MFC的線程被自己外部創(chuàng)建的線程調(diào)用就會有這個錯誤。解決的方法就是在線程里用SendMessage給窗口發(fā)送一個自定義的消息。比如我這里用的就是 要實現(xiàn)這樣的方法在BEGIN_MESSAGE_MAP下面要添加ON_MESSAGE(WM_UpdateDATA, OnMyMessage) 綁定我自己的消息,這樣外部的線程就可以通過SendMessage來調(diào)用窗體的函數(shù)OnMy
4、Message了,用來顯示信息WM_UpdateDATA在stdafx.h中我自己定義為#define WM_UpdateDATA WM_USER+100 。定義一個自己的消息*/這樣只要外部給窗口SendMessage -> WM_UpdateDATA 就會讓窗口的onmymessage函數(shù)執(zhí)行了,具體要顯示的內(nèi)容放在一個全局變量就好了。 serverdlg.cpp 二、程序代碼/定義全局變量,方便各個工作線程和窗口線程的通信SOCKETsockuse,sock;int flag; /主要是用來標志是否連接,用來控制一些循環(huán)和功能HWND m
5、ydlg; /記錄窗體的句柄HWND stopbutton; /記錄關(guān)閉連接按鈕的句柄 char buff200; /主要的緩沖區(qū),顯示數(shù)據(jù)使用,也供線程間通信使用 CWinThread* mainthread; /記錄線程的句柄 UINT FileTrans(LPVOID pParam) /發(fā)送文件的線程函數(shù) 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) /判斷文件路徑取得是否正確 /若失敗則恢復(fù)建立MainControl線程,進行數(shù)據(jù)接收處理。 mainthread=AfxBeginThread(MainControl,NULL); flag=1; /設(shè)定flag保證MainControl可以正常進行循環(huán)return 0; memset(filebuff,0,100); /清空filebuff strcpy(path,ofn.lpstrFile); /將路徑拷貝到path中 strcpy(filebuff,"send
8、start"); send(sockuse,filebuff,strlen(filebuff),0); /發(fā)送請求信息 recv(sockuse,filebuff,100,0); /等待接收反饋信息 if(strcmp(filebuff,"sendagree") /若接收到的信息不是sendagree strcpy(buff,"對方接受錯誤"); SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息 mainthread=AfxBeginThread(MainControl,NULL
9、); /恢復(fù)建立MainControl線程 flag=1; /設(shè)定flag保證MainControl循環(huán)正常 return 0;SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息 while(!feof(fp) num=fread(filebuff,1,100,fp); /文件結(jié)束前每次讀取100字節(jié) send(sockuse,filebuff,num,0); /發(fā)送,最后一次不足100字節(jié) /作為標志,可以讓接受方知道文件結(jié)束 strcpy(buff,"發(fā)送完畢");SendMess
10、age(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息memset(buff,0,100);fclose(fp); /關(guān)閉文件 flag=1; return 0; UINT FileRecv() /接收文件函數(shù) char sendbuff100; /發(fā)送緩沖區(qū)char recvbuff100; /接受緩沖區(qū)int num; /記錄每次接受的字節(jié)數(shù) 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); /發(fā)送sendagree告知對方開始發(fā)送吧 SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯/示信息 num=recv(sockuse,recvbuff,100,0); /接收數(shù)據(jù)FILE *fp=fopen(szGetName,"wb"); /打開文件,路徑為szGe
13、tNamefwrite(recvbuff,num,1,fp); /寫入之前的數(shù)據(jù) num=recv(sockuse,recvbuff,100,0); fwrite(recvbuff,num,1,fp); fclose(fp); /結(jié)束后關(guān)閉文件。 strcpy(buff,"接收完畢");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,/顯示信息return 0; int Addrlen=sizeof(sockaddr_in); /accept要用到的數(shù)值sockaddr_i
14、n ClientAddr; sockuse=accept(sock,(struct sockaddr FAR *)&ClientAddr,&Addrlen);/上一句的accept函數(shù)調(diào)用后會進行阻塞,造成未返回時程序假死,使用了單獨的線程/就是為了防止這樣的現(xiàn)象發(fā)生flag=1; /返回成功后,設(shè)定flag保證MainControl循環(huán)正常strcpy(buff,"已經(jīng)連接!");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消/息,顯示信息mainthread=AfxBeginThread(MainC
15、ontrol,NULL); /建立MainControl線程 return 0; char recvbuff100; /接受緩沖區(qū)memset(buff,0,100);while(flag)memset(recvbuff,0,100); /每次接收前清空緩沖區(qū) recv(sockuse,recvbuff,100,0); /進行阻塞接收數(shù)據(jù),如果不是用單獨的線程/會造成程序假死,這也就是為什么我的程序使用單獨的線程來處理 if(!strcmp(recvbuff,"end")SendMessag
16、e(stopbutton,BM_CLICK,NULL,NULL); /消息判斷為end則調(diào)用/窗口的OnButtonEnd函數(shù)來結(jié)束連接的清理工作。return 0;else if(!strcmp(recvbuff,"sendstart")/接受的消息判斷為"sendstart"則調(diào)用FileRecv() FileRecv(); else strcpy(buff,"client:");strcat(buff,recvbuff);SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給/窗口消息,
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 創(chuàng)建錯誤!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(); /取得窗口句柄供線程函數(shù)使用stopbutton=:GetDlgItem(mydlg,IDC_BUTT
20、ON_STOP); /取得關(guān)閉連接按/鈕的句柄供線程函數(shù)使用 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); /給對方發(fā)送信息告知結(jié)束TerminateThread(mainthread->m_hThread,0x01); /結(jié)束MainControl線程closesocket(sock); /關(guān)閉套接字closesocket(sockuse);WSACleanup(); /清理網(wǎng)絡(luò) m_ctrlstop.EnableWindow(false); /使能一些按鈕m_ctrlfile.EnableWindow(false);m_ctrlstart.Enabl
22、eWindow(true); void CServerDlg:OnMyMessage() showmess(buff); /僅僅是為了線程函數(shù)調(diào)用內(nèi)部的信息顯示函數(shù) void CServerDlg:OnButtonFile() flag=0; /設(shè)定 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); /取得對話框數(shù)據(jù)send(sockuse,talkbuff,100,0); /發(fā)送給對方信息 clientDlg.cpp 首先是clientDlg.cpp #include "winsock.h" #include "stdio.h"#include"string.h"#pragm
24、a comment(lib,"wsock32.lib") /這四句要加在本文件的開頭部分。保證/網(wǎng)絡(luò)功能正常在BEGIN_MESSAGE_MAP下面要添加ON_MESSAGE(WM_UpdateDATA, OnMyMessage) /*綁定我自己的消息,這樣外部的線程就可以通過SendMessage來調(diào)用窗體的函數(shù)OnMyMessage了,用來顯示信息 WM_UpdateDATA在stdafx.h中我自己定義為#define WM_UpdateDATA WM_USER+100 /定義一個自己的消息*/ /定義全局變量,方便各個工
25、作線程和窗口線程的通信SOCKET sock;char buff100; /主要的緩沖區(qū),顯示數(shù)據(jù)使用,也供線程間通信使用HWND mydlg; /記錄窗體的句柄sockaddr_in ServerAddr;int flag=0; /主要是用來標志是否連接,用來控制一些循環(huán)和功能CWinThread* mainthread;HWND stopbutton; /記錄關(guān)閉連接按鈕的句柄 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); /發(fā)送給窗口消/息,顯示信息closesocket(sock); /失敗則關(guān)閉sockreturn 0;strcpy(buff,"已經(jīng)連接!");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL);/發(fā)送給窗口消/息,顯示信息flag=1;mainthread=AfxBeginThre
27、ad(MainControl,NULL); /建立MainControl線程return 0; char sendbuff100; /發(fā)送緩沖區(qū)char recvbuff100; /接受緩沖區(qū)int num; /記錄每次接受的字節(jié)數(shù) 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); /發(fā)送sendagree告知對方開始發(fā)送吧 SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息 num=recv(sock,recvbuff,100,0); /接收數(shù)據(jù)FILE *fp=fopen(szGetName,"wb"); /打開文件,路徑為szGetNamefwrite(recvbuff,num,1,fp); /寫入之前的數(shù)據(jù) while(num=100) /根據(jù)接收是否為100字節(jié)判斷文件是否結(jié)束 num=recv(sock,recvbuff,100,0)
30、; fwrite(recvbuff,num,1,fp); strcpy(buff,"接收完畢");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息return 0; char recvbuff100;/接受緩沖區(qū) memset(buff,0,100); while(flag) memset(recvbuff,0,100); /每次接收前清空緩沖區(qū) recv(sock,recvbuff,100,0); /進行阻塞接收數(shù)據(jù),如果不是用單獨的線程/
31、會造成程序假死,這也就是為什么我的程序使用單獨的線程來處理 if(!strcmp(recvbuff,"end") /消息判斷為end則調(diào)用/窗口的OnButtonEnd函數(shù)來結(jié)束連接的清理工作。SendMessage(stopbutton,BM_CLICK,NULL,NULL); return 0;else if(!strcmp(recvbuff,"sendstart")/接受的消息判斷為"sendstart"則調(diào)用FileRecv() FileRecv();memset(recvbuff,0,100); else strcpy(bu
32、ff,"server:");strcat(buff,recvbuff);SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息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) /判斷文件路徑取得是否正確 /若失敗則恢復(fù)建立MainControl線程,進行數(shù)據(jù)接收處理。 mainthread=AfxBeginThread(MainControl,NULL); flag=1; /設(shè)定flag保證MainControl可以正常進行循環(huán)return 0; memset(filebuff,0,100); /清空filebuff strcpy(path,ofn.lpstrFile); /將路徑拷貝到path中 strcpy(filebuff,"sendstart"); send(sock,filebuff,strlen(filebu
35、ff),0); /發(fā)送請求信息 recv(sock,filebuff,100,0); /等待接收反饋信息 if(strcmp(filebuff,"sendagree") /若接收到的信息不是sendagree strcpy(buff,"對方接受錯誤"); SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息 mainthread=AfxBeginThread(MainControl,NULL); /恢復(fù)建立MainControl線程 flag=1; /設(shè)定flag保證MainControl循環(huán)
36、正常 return 0; strcpy(buff,"對方已經(jīng)同意,開始發(fā)送文件"); SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息 FILE *fp=fopen(path,"rb"); /打開文件 while(!feof(fp) num=fread(filebuff,1,100,fp); /文件結(jié)束前每次讀取100字節(jié) send(sock,filebuff,num,0); /發(fā)送,最后一次不足100字節(jié) /
37、作為標志,可以讓接受方知道文件結(jié)束 strcpy(buff,"發(fā)送完畢");SendMessage(mydlg,WM_UpdateDATA,NULL,NULL); /發(fā)送給窗口消息,顯示信息memset(buff,0,100);fclose(fp); /關(guān)閉文件 mainthread=AfxBeginThread(MainControl,NULL); /恢復(fù)建立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地址,默認值 /取得窗口句柄供線程函數(shù)使用flag=0;stopbutton=:GetDlgItem(mydlg,IDC_BUTTON_STOP);/取得關(guān)閉連接按鈕的句柄供線程函數(shù)使用AfxBeginThread(WaitForConn
40、ect,NULL);/啟動WaitForConnect線程進行連接return; m_strmess+=mess;m_strmess+="rn" /在每條信息后添加回車UpdateData(false); /更新信息顯示 void CClientDlg:OnMyMessage() showmess(buff);/僅僅是為了線程函數(shù)調(diào)用內(nèi)部的信息顯示函數(shù) TerminateThread(mainthread,0x01);/結(jié)束MainControl線程strcpy(buff,"end");send(sock,buff,strlen(buff),0);/給對方發(fā)送信息告知結(jié)束closesocket(sock);/關(guān)閉套接字 WSACleanup();m_ctrlstop.EnableWindow(false);/使一些按鈕m_ctrlfile.EnableWindow(false);m_ctrlconnect.EnableWindow(true);
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 房地產(chǎn)委托代持合同樣本
- 銷售合同模板:分期付款協(xié)議指南
- 酒店裝修項目工程合同
- 建筑工程合同爭議處理:案例分析及展望
- 包裝設(shè)計師試題及答案
- 電導率儀操作規(guī)程
- 植物學試題+參考答案
- 裝飾裝修工程分包合同模板
- 環(huán)保清潔工程合同書樣本
- GB/T 31190-2014實驗室廢棄化學品收集技術(shù)規(guī)范
- 《地鐵突發(fā)大客流應(yīng)急管理》論文11000字
- 第五章-項目時間管理課件
- 導游人員管理法律制度課件
- 木箱檢驗作業(yè)指導書
- 初中級檔案職稱《檔案事業(yè)概論》檔案事業(yè)題庫一
- 美國地圖高清中文版
- 《中國特色社會主義理論與實踐研究》課程教學大綱
- 金屬監(jiān)督監(jiān)理實施細則
- DB13T 1606-2012 糧食作物種子 谷子雜交種
- DB33-T1247-2021《城市河道景觀設(shè)計標準》
評論
0/150
提交評論