代碼1分析:
說明:對(duì)于單核cpu的電腦來說,線程都在自己的時(shí)間片中運(yùn)行,單位時(shí)間內(nèi),系統(tǒng)只能運(yùn)行一個(gè)線程,交替運(yùn)行;對(duì)于多核cpu或多cpu的電腦來說,才是真正意義上的單位時(shí)間內(nèi)運(yùn)行多個(gè)線程
說明:如果采用向?qū)?chuàng)建的Win32 Console程序選擇的是空項(xiàng)目時(shí),則以下使用方式是正確的,如果選擇是”hello world”的簡單應(yīng)用程序時(shí),則在main函數(shù)中想要使用輸入輸出流,則必須使用:
#include <iostream>
using namespace std;
#include <windows.h> //調(diào)用API函數(shù)
#include <iostream.h>
說明:程序有一個(gè)主線程,入口函數(shù)main,在主線程上可以創(chuàng)建新線程,入口函數(shù)定義成如下形式:
DWORD WINAPI ThreadProc1(LPVOID lpParameter); //函數(shù)聲明
void main()
{
HANDLE handle=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); //創(chuàng)建新線程
說明:調(diào)用CloseHandle函數(shù)并沒有終止新創(chuàng)建的線程,只是表明在主線程中對(duì)新創(chuàng)建的線程的引用不是很感興趣而已,所以在沒有在做什么動(dòng)作之前就關(guān)閉了,這個(gè)函數(shù)在主線程運(yùn)行過程中必須調(diào)用,否則即使對(duì)應(yīng)線程運(yùn)行結(jié)束,對(duì)應(yīng)句柄也是不會(huì)關(guān)閉的,知道主線程退出時(shí),才會(huì)由系統(tǒng)集中清理。
CloseHandle(handle);
cout<<"main thread is running"<<endl;
說明:如果不添加Sleep語句,主線程會(huì)在自己的時(shí)間片中運(yùn)行完成后(該時(shí)間片在main函數(shù),也就是主線程全部執(zhí)行完畢后還有時(shí)間剩余),選擇直接退出,主線程都退出了,依附于主線程的新線程也就不會(huì)有機(jī)會(huì)得到執(zhí)行了,只有讓主線程暫停執(zhí)行(采用sleep函數(shù)),即掛起,讓出執(zhí)行的權(quán)利,操作系統(tǒng)會(huì)從等待的線程中選擇一個(gè)來運(yùn)行,那么新創(chuàng)建的線程得到機(jī)會(huì)執(zhí)行
Sleep(10);
}
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
cout<<"thread1 is running"<<endl;
return 0;
}
輸出結(jié)果:
main thread is running
thread1 is running
代碼2分析:
添加全局變量
int index=0;
將main函數(shù)中輸出語句修改為:
while(index++<50)
cout<<"main thread is running"<<endl;
將線程中輸出語句修改為:
while(index++<50)
cout<<"thread1 is running!"<<endl;
將main函數(shù)中sleep語句省去
則得到的結(jié)果是:
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
thread1 is running!
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
main thread is running
結(jié)果說明主線程和副線程在交替運(yùn)行,也就是主線程在它的時(shí)間片運(yùn)行結(jié)束后,副線程得到執(zhí)行的權(quán)利,在它自己所對(duì)應(yīng)的時(shí)間片中運(yùn)行,此時(shí)主線程其實(shí)還沒有運(yùn)行結(jié)束,它將等待著副線程運(yùn)行結(jié)束后繼續(xù)執(zhí)行
代碼3分析(車票的銷售):
#include <windows.h>
#include <iostream.h>
DWORD WINAPI ThreadProc1(LPVOID lpParameter);
DWORD WINAPI ThreadProc2(LPVOID lpParameter);
int ticket=50;
void main()
{
HANDLE handle1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
HANDLE handle2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
CloseHandle(handle1);
CloseHandle(handle2);
說明:為了使得主線程在退出之前保證副進(jìn)程的執(zhí)行完成,有些實(shí)現(xiàn)方法是采用恒真的空循環(huán),單此種方法主線程會(huì)占用cpu的運(yùn)行時(shí)間,如果采用Sleep,則主線程完全不占用cpu的任何運(yùn)行時(shí)間
Sleep(4000);
}
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
說明:在線程的時(shí)間片內(nèi)持續(xù)運(yùn)行
while(TRUE)
{
if(ticket>0)
cout<<"thread1 sale the ticket id is:"<<ticket--<<endl;
else
break;
}
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
while(TRUE)
{
if(ticket>0)
cout<<"thread2 sale the ticket id is:"<<ticket--<<endl;
else
break;
}
return 0;
}
輸出結(jié)果:
thread1 sale the ticket id is:50
thread1 sale the ticket id is:49
thread1 sale the ticket id is:48
thread1 sale the ticket id is:47
thread1 sale the ticket id is:46
thread1 sale the ticket id is:45
thread1 sale the ticket id is:44
thread1 sale the ticket id is:43
thread1 sale the ticket id is:42
thread1 sale the ticket id is:41
thread1 sale the ticket id is:40
thread1 sale the ticket id is:39
thread1 sale the ticket id is:38
thread1 sale the ticket id is:37
thread1 sale the ticket id is:36
thread1 sale the ticket id is:35
thread1 sale the ticket id is:35
thread2 sale the ticket id is:34
thread2 sale the ticket id is:33
thread2 sale the ticket id is:32
thread2 sale the ticket id is:31
thread2 sale the ticket id is:30
thread2 sale the ticket id is:29
thread2 sale the ticket id is:28
thread2 sale the ticket id is:27
thread2 sale the ticket id is:26
thread2 sale the ticket id is:25
thread2 sale the ticket id is:24
thread2 sale the ticket id is:23
thread2 sale the ticket id is:22
thread2 sale the ticket id is:21
thread2 sale the ticket id is:20
thread2 sale the ticket id is:19
thread1 sale the ticket id is:18
thread1 sale the ticket id is:17
thread1 sale the ticket id is:16
thread1 sale the ticket id is:15
thread1 sale the ticket id is:14
thread1 sale the ticket id is:13
thread1 sale the ticket id is:12
thread1 sale the ticket id is:11
thread1 sale the ticket id is:10
thread1 sale the ticket id is:9
thread1 sale the ticket id is:8
thread1 sale the ticket id is:7
thread1 sale the ticket id is:6
thread1 sale the ticket id is:5
thread1 sale the ticket id is:4
thread1 sale the ticket id is:3
thread1 sale the ticket id is:2
thread1 sale the ticket id is:1
我們發(fā)現(xiàn)結(jié)果一切正常,但是有可能會(huì)遇到如下一種情況:
當(dāng)ticket數(shù)量運(yùn)行到1時(shí),線程1正在運(yùn)行,此時(shí)線程1運(yùn)行到輸出語句時(shí),它的時(shí)間片已經(jīng)結(jié)束,則線程1對(duì)ticket id的減減動(dòng)作沒有完成,此時(shí)線程2開始執(zhí)行,發(fā)現(xiàn)數(shù)量是1,則執(zhí)行減減動(dòng)作,使得數(shù)量為0,返回,線程1繼續(xù)執(zhí)行,此時(shí)票的數(shù)量已經(jīng)是0了,線程1繼續(xù)執(zhí)行輸出語句,對(duì)票的數(shù)量執(zhí)行減減,則數(shù)量變?yōu)?/span>-1,這是不允許的。這是由于搶占全局的資源所引起的。
解決這個(gè)問題的辦法是實(shí)現(xiàn)線程間的“同步”,即一個(gè)線程在對(duì)一個(gè)全局的資源進(jìn)行操作的過程中,是不允許其他線程對(duì)全局的資源進(jìn)行訪問,直到該線程對(duì)資源操作完畢后。
涉及概念:互斥對(duì)象
n 互斥對(duì)象(mutex)屬于內(nèi)核對(duì)象,它能夠確保線程擁有對(duì)單個(gè)資源的互斥訪問權(quán)。
n 互斥對(duì)象包含一個(gè)使用數(shù)量,一個(gè)線程ID和一個(gè)計(jì)數(shù)器。
n ID用于標(biāo)識(shí)系統(tǒng)中的哪個(gè)線程當(dāng)前擁有互斥對(duì)象,計(jì)數(shù)器用于指明該線程擁有互斥對(duì)象的次數(shù)。
涉及到三個(gè)函數(shù):
1. CreateMutex:創(chuàng)建互斥對(duì)象,返回互斥對(duì)象的句柄,其中第二個(gè)參數(shù)如果是TRUE,表示調(diào)用該函數(shù)的線程擁有互斥對(duì)象的所有權(quán),即互斥對(duì)象處于非空閑狀態(tài)。如果是FALSE,則表示當(dāng)前互斥對(duì)象處于空閑狀態(tài),其他線程可以占用。
2. WaitForSingleObject:等待互斥對(duì)象的使用權(quán),如果第二個(gè)參數(shù)設(shè)置為INFINITE,則表示會(huì)持續(xù)等待下去,直到擁有所有權(quán),才有權(quán)執(zhí)行該函數(shù)下面的語句。一旦擁有了所有權(quán),則會(huì)將互斥對(duì)象的的線程ID設(shè)置為當(dāng)前使用的線程ID值。
3. ReleaseMutex:將互斥對(duì)象所有權(quán)進(jìn)行釋放,交還給系統(tǒng)。
可以將互斥對(duì)象想象成一把鑰匙,CreateMutex創(chuàng)建了這把鑰匙,WaitForSingleObject等待這把鑰匙去訪問一個(gè)公共的資源,比如一個(gè)房間,如果擁有了鑰匙,則這個(gè)房間的所有權(quán)就屬于這個(gè)進(jìn)程了,別人是進(jìn)不去這個(gè)房間的,直到進(jìn)程將這個(gè)房間的鑰匙歸還掉,即ReleaseMutex。
具體實(shí)現(xiàn)代碼:
#include <windows.h>
#include <iostream.h>
DWORD WINAPI ThreadProc1(LPVOID lpParameter);
DWORD WINAPI ThreadProc2(LPVOID lpParameter);
int ticket=100;
HANDLE hMutex;
void main()
{
HANDLE handle1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
HANDLE handle2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
CloseHandle(handle1);
CloseHandle(handle2);
hMutex=CreateMutex(NULL,FALSE,NULL); //第二個(gè)參數(shù)為FALSE,將互斥對(duì)象聲明為空閑狀態(tài)
Sleep(4000);
}
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
while(TRUE)
{
WaitForSingleObject(hMutex,INFINITE); //第二個(gè)參數(shù)為INFINITE表示一直等待,直到擁有互斥對(duì)象
if(ticket>0)
{
Sleep(1);
cout<<"thread1 sale the ticket id is:"<<ticket--<<endl;
}
else
break;
ReleaseMutex(hMutex); //使用完了,將互斥對(duì)象還給操作系統(tǒng)
}
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
while(TRUE)
{
WaitForSingleObject(hMutex,INFINITE);
if(ticket>0)
{
Sleep(1);
cout<<"thread2 sale the ticket id is:"<<ticket--<<endl;
}
else
break;
ReleaseMutex(hMutex);
}
return 0;
}
思考:如果將WaitForSingleObject和ReleaseMutex放置在while循環(huán)的外部,發(fā)現(xiàn)程序的輸出結(jié)果是只有線程1在銷售票,線程2沒有銷售一張票,這是因?yàn)榫€程1在得到互斥對(duì)象的所有權(quán)后,進(jìn)入到了循環(huán),而釋放互斥權(quán)的調(diào)用必須等到循環(huán)執(zhí)行結(jié)束,即使線程1在時(shí)間片完成后,將執(zhí)行權(quán)交給了線程2,單線程2發(fā)現(xiàn)互斥對(duì)象的所有權(quán)還是被占用著,所以沒有做任何動(dòng)作,線程1繼續(xù)執(zhí)行,直到將票銷售完,退出循環(huán),釋放互斥對(duì)象的所有權(quán),此時(shí)線程2在得到所有權(quán)后發(fā)現(xiàn)票已經(jīng)銷售一空,也就退出了。在上述代碼中,為什么輸出結(jié)果正確呢,也就是線程1和2交替售票,這是因?yàn)榫€程1得到互斥對(duì)象的控制權(quán)后執(zhí)行單張票的銷售動(dòng)作,動(dòng)作完成就立即釋放了控制權(quán)。在線程1的執(zhí)行時(shí)間片完成后,線程2就開始執(zhí)行了,線程2執(zhí)行和線程1相同的動(dòng)作。
兩段代碼最主要的區(qū)別就是前者在銷售所有票的過程中都獨(dú)占著互斥對(duì)象資源,而后者是銷售完一張票后就將互斥對(duì)象資源釋放掉了。
幾點(diǎn)思考:
1. 在main函數(shù)中將CreateMutex 的第二個(gè)參數(shù)修改為TRUE,則表示主線程在創(chuàng)建互斥對(duì)象的時(shí)候就擁有了所有權(quán),執(zhí)行代碼,發(fā)現(xiàn)線程1和2都沒有執(zhí)行,只是因?yàn)橹骶€程沒有釋放掉互斥對(duì)象。考慮在線程1和2的WaitForSingleObject前添加ReleaseMutex可否?也就是我在申請(qǐng)前將別人占用的互斥對(duì)象釋放掉,這顯然是不行的,不然就失去互斥的真正意義了,如果我自己想用,就將別人踢掉,就失去了規(guī)則了。那內(nèi)部是怎么實(shí)現(xiàn)的呢?在CreateMutex或WaitForSingleObject申請(qǐng)到互斥對(duì)象的所有權(quán)后,會(huì)將互斥對(duì)象中的線程ID設(shè)置為調(diào)用線程的ID,ReleaseMutex時(shí)會(huì)比較當(dāng)前的線程ID和互斥對(duì)象中的ID是否一致,如果不一致,則禁止釋放。所以要想線程1和2能正常執(zhí)行,則必須在CreateMutex后調(diào)用ReleaseMutex。
2. 在main函數(shù)中CreateMutex的第二個(gè)參數(shù)修改為TRUE后,互斥對(duì)象的所有權(quán)由主線程所有,然后調(diào)用WaitForSingleObject,發(fā)現(xiàn)申請(qǐng)依然成功,這是因?yàn)閾碛兴袡?quán)的線程是自身線程,單互斥對(duì)象的內(nèi)部發(fā)生了變化,它內(nèi)部的計(jì)數(shù)器設(shè)置成了2,也就是如果希望線程1和2能執(zhí)行,則需要兩次調(diào)用ReleaseMutex。ReleaseMutex函數(shù)的意義就是將計(jì)數(shù)器遞減。
3. 如果在線程中調(diào)用WaitForSingleObject后沒有調(diào)用ReleaseMutex,則該線程執(zhí)行終止后(不是單次時(shí)間片完成后),操作系統(tǒng)會(huì)自動(dòng)釋放掉互斥對(duì)象
讓程序單位時(shí)間內(nèi)只能運(yùn)行一個(gè)實(shí)例,也就是如果實(shí)例存在,則不打開新的實(shí)例:
由CreateMutex在MSDN中的介紹可以知道,只需要將該函數(shù)的第三個(gè)參數(shù),Mutex對(duì)象的名字不設(shè)置成NULL,即給它取個(gè)名字,然后代碼修改如下:
hMutex=CreateMutex(NULL,FALSE,”instance”); //Mutex的名稱可以任意的取
if(hMutex)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"the application instance is exit!"<<endl;
return;
}
}
創(chuàng)建基于多線程的聊天室程序:
1. 創(chuàng)建一個(gè)基于對(duì)話框的MFC程序,界面如下:
2. 添加套接字庫頭文件:
調(diào)用MFC的內(nèi)置函數(shù):AfxSocketInit,該函數(shù)其實(shí)也是調(diào)用Win32中的WSAStartup(見上一章的介紹),并且是調(diào)用1.1的套接字庫版本,該函數(shù)能確保程序終止前調(diào)用WSACleanup的調(diào)用,該函數(shù)的放置位置最好在CWinApp中的InitInstance中,注意包含頭文件Afxsock.h,在StdAfx.h這個(gè)頭文件中進(jìn)行包含。StdAfx.h頭文件是一個(gè)預(yù)編譯頭文件,在該文件中包含了MFC程序運(yùn)行的一些必要的頭文件,如afxwin.h這樣的MFC核心頭文件等。它是第一個(gè)被程序加載的文件。
3. 加載套接字庫:
在CWinApp中的InitInstance添加如下代碼:
if(FALSE==AfxSocketInit())
{
AfxMessageBox("套接字庫加載失??!");
return FALSE;
}
4. 創(chuàng)建套接字,將自己假想成服務(wù)器端,進(jìn)行套接字和地址結(jié)構(gòu)的綁定,等待別人發(fā)送消息過來。
在CDialog中
添加成員變量:SOCKET m_socket
添加自定義函數(shù):
BOOL CChatDlg::InitSocket()
{
m_socket=socket(AF_INET,SOCK_DGRAM,0); //UDP連接方式
if(INVALID_SOCKET==m_socket)
{
MessageBox("套接字創(chuàng)建失??!");
return FALSE;
}
SOCKADDR_IN addrServer; //將自己假想成server
addrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons(1234);
int retVal;
retVal=bind(addrSock,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
if(SOCKET_ERROR==retVal)
{
closesocket(addrSock);
MessageBox("套接字綁定失??!");
return FALSE;
}
return TRUE;
}
5. 在CChatDlg類的外部添加結(jié)構(gòu)體:
struct RECVPARAM
{
說明:因?yàn)樘捉幼直旧碇簧婕皞鬏數(shù)膮f(xié)議類型,是UDP還是TCP,而和是服務(wù)器端還是客戶端沒有必然的關(guān)系,所以本程序即涉及到服務(wù)器端又涉及到客戶端,采用同一個(gè)套接字是被允許的。
SOCKET sock; //保存最初創(chuàng)建的套接字
HWND hWnd; //保存對(duì)話框的窗口句柄
};
6. 在對(duì)話框的初始化代碼中完成線程的創(chuàng)建:
在CChatDlg::OnInitDialog函數(shù)中添加下面的代碼:
if(!InitSocket()) //服務(wù)器端的創(chuàng)建
return FALSE;
RECVPARAM *pRecvParam=new RECVPARAM;
pRecvParam->hWnd=m_hWnd;
pRecvParam->sock=m_socket;
說明:
1. 接收部分應(yīng)該一直處于響應(yīng)狀態(tài),如果和發(fā)送部分放在同一段代碼中,勢(shì)必會(huì)阻塞掉發(fā)送功能的實(shí)現(xiàn),所以考慮將接收放在單獨(dú)的線程中,使它在一個(gè)while循環(huán)中,始終處于響應(yīng)狀態(tài)
2. 因?yàn)樾枰獋鬟f兩個(gè)參數(shù)進(jìn)去,一個(gè)是recvfrom需要用的套接字,另一個(gè)是當(dāng)收到數(shù)據(jù)后需要將數(shù)據(jù)顯示在窗口中的對(duì)應(yīng)文本框控件上,所以需要傳遞當(dāng)前窗口的句柄,但CreateThread方法只能傳遞一個(gè)參數(shù),即第四個(gè)參數(shù),這時(shí)候就想到了采用結(jié)構(gòu)體的方式傳遞。
HANDLE hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);
CloseHandle(hThread);
7. 創(chuàng)建線程入口函數(shù)RecvProc:
可模仿ThreadProc的創(chuàng)建方式(在MSDN中有原型),但遇到一個(gè)問題,將該函數(shù)申明為CChatDlg的成員函數(shù)嘛?答案不是的,因?yàn)槿绻浅蓡T函數(shù)的話,那它屬于某個(gè)具體的對(duì)象,那么在調(diào)用它的時(shí)候勢(shì)必要讓程序創(chuàng)建一個(gè)對(duì)象,但該對(duì)象的構(gòu)造函數(shù)有參數(shù)的話,系統(tǒng)就不知所措了,所以可以將函數(shù)創(chuàng)建為全局函數(shù),即不屬于類,但這失去了類的封裝性,最好的方法是將該方法聲明為靜態(tài)方法,它不屬于任何一個(gè)對(duì)象。
在CChatDlg類的頭文件中添加:
static DWORD WINAPI RecvProc(LPVOID lpParameter);
在cpp文件中添加:
DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)
{
RECVPARAM* pRecvParam=(RECVPARAM*)lpParameter;
HWND hWnd=pRecvParam->hWnd;
SOCKET sock=pRecvParam->sock;
char recvBuf[200];
char resultBuf[200];
SOCKADDR_IN addrFrom; //這個(gè)時(shí)候是假想成服務(wù)器端
int len=sizeof(SOCKADDR_IN);
while(TRUE) //處于持續(xù)響應(yīng)狀態(tài)
{
int retVal=recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len); //從客戶端接收數(shù)據(jù),并將客戶端的地址結(jié)構(gòu)體填充
if(SOCKET_ERROR == retVal)
{
AfxMessageBox("接收數(shù)據(jù)出錯(cuò)"); //因?yàn)楸竞瘮?shù)是靜態(tài)函數(shù),所以只能調(diào)用全局的消息了
break;
}
else
{
sprintf(resultBuf,"%s said:%s",inet_ntoa(addFrom.sin_addr),recvBuf);
//現(xiàn)在已經(jīng)拿到客戶端送過來的消息了,但因?yàn)樽陨硎庆o態(tài)函數(shù),所以拿不到當(dāng)前窗口對(duì)象中的控件的句柄,也就不能對(duì)其賦值了,唯一辦法就是用消息的形式將接收到的值拋出到窗口的消息隊(duì)列中,等待消息處理
::PostMessage(hWnd,WM_RECVDATA,0,(LPARAM)resultBuf); }
}
return 0;
}
8. 自定義消息:
定義自定義消息的宏:
#define WM_RECVDATA WM_USER+1
聲明消息響應(yīng)函數(shù):因?yàn)橛袇?shù)要傳遞,所以wParam和lParam都要寫,如果沒有參數(shù)需要傳遞,可以不寫
afx_msg void OnRecvData(WPARAM wParam,LPARAM lParam);
消息映射:
ON_MESSAGE(WM_RECVDATA,OnRecvData)
定義消息響應(yīng)函數(shù):
void CChatDlg::OnRecvData(WPARAM wParam,LPARAM lParam)
{
//注意將文本框的屬性設(shè)置成多行
CString recvData=(char*)lParam;
CString temp; //文本框中現(xiàn)有的內(nèi)容
GetDlgItemText(IDC_EDIT_RECV,temp);
temp+="\r\n";
temp+=recvData;
SetDlgItemText(IDC_EDIT_RECV,temp);
}
自此,消息的接收和顯示部分已經(jīng)完成了
9. 消息的發(fā)送:
在發(fā)送按鈕點(diǎn)擊的響應(yīng)函數(shù)中添加:
DWORD dword;
CIPAddressCtrl* pIPAddr=(CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1);
pIPAddr->GetAddress(dword);
//因?yàn)閷?duì)方有具體的IP地址值,我們假想對(duì)方是服務(wù)器端。在發(fā)送的時(shí)候程序就從服務(wù)器的角色轉(zhuǎn)變?yōu)榭蛻舳肆?/span>
SOCKADDR_IN addrServer;
addrServer.sin_addr.S_un.S_addr=htonl(dword);
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons(1234);
CString strSend;
GetDlgItemText(IDC_EDIT_SEND,strSend);
sendto(m_socket,strSend,strlen(strSend)+1,0,(SOCKADDR*)&addrServer,sizeof(SOCKADDR));
SetDlgItemText(IDC_EDIT_SEND,"");
幾點(diǎn)思考:
1. 本程序的核心在于將消息的發(fā)送的和接收發(fā)在了兩個(gè)不同的線程中,接收放在新創(chuàng)建的副進(jìn)程中,因?yàn)槠湟恢碧幱陧憫?yīng)狀態(tài),也就是需要一個(gè)while循環(huán);發(fā)送放在主線程中。這樣消息的接收和發(fā)送就不存在先后順序了,且一直處于循環(huán)中的接收也不會(huì)影響到發(fā)送。
2. 上述代碼中的新線程入口函數(shù)中可能沒有必要傳遞兩個(gè)參數(shù)進(jìn)去,其中SOCKET參數(shù)可以在入口函數(shù)內(nèi)部創(chuàng)建,反正SOCKET變量也就是聲明是TCP還是UDP,和發(fā)送或接收沒有必然的聯(lián)系,如果這樣的話,就沒有必要聲明第五步中的結(jié)構(gòu)體了,CreateThread方法也剛好傳遞一個(gè)參數(shù),即當(dāng)前窗口的句柄
聯(lián)系客服