轉(zhuǎn)自:http://www.cnblogs.com/jsjkandy/archive/2008/08/12/1266345.html
一、前言
在前篇《GPRS開發(fā)系列文章之進(jìn)階篇》里,我主要詳細(xì)講解了客戶端進(jìn)行GPRS連接的常用API,并對(duì)GPRSdemo測(cè)試程序中的連接類ConnManager中的一些重要函數(shù)做了說(shuō)明,最后稍微提及了下服務(wù)器端要用到的一些類庫(kù)。今天,在這篇實(shí)戰(zhàn)篇中,我將在理解前兩篇的基礎(chǔ)上,結(jié)合客戶端與服務(wù)器端,向大家介紹這篇GPRS開發(fā)之實(shí)戰(zhàn)篇,向大家演示如何利用GPRS開發(fā)一個(gè)客戶端與服務(wù)器端互相通信的程序,主要介紹SOCKET編程的原理和SOCKET應(yīng)用API,并在最后提供本實(shí)戰(zhàn)篇的源代碼下載。最后還是那句老話,歡迎指點(diǎn),共同提高!
二、實(shí)戰(zhàn)系列篇詳解
1. 開發(fā)環(huán)境
a) 客戶端:EVC4;
b) 服務(wù)器端:VS2005(C#);
2. 運(yùn)行環(huán)境
a) 客戶端:ppc 2003(winCE4.2) for mobile或以上版本;
b) 服務(wù)器端:WINXP/SERVER 2003等
3. 客戶端和服務(wù)器端通信詳解
本文章的主要目的是利用GPRS連接編寫一個(gè)利用TCP協(xié)議進(jìn)行通信的程序,而在上篇文章已解決了GPRS連接的問(wèn)題,因此剩下的主要就是我們都比較熟悉的SOCKET編程了,由于客戶端和服務(wù)器端進(jìn)行SOCKET通信的原理相同,所以我將他們放到一起進(jìn)行講解,主要介紹SOCKET編程的一些原理及要點(diǎn),然后貼出部分比較重要的代碼供大家參考。
首先,介紹些要了解SOCKET編程的一些核心概念:
我們知道在這種通信程序中,一般客戶端和服務(wù)器端是分開的(本機(jī)通信可以看作是一種特例),客戶端一旦和服務(wù)器端建立連接成功后就可以透明的傳輸數(shù)據(jù)和接收數(shù)據(jù)了。那么我們的程序在建立了GPRS連接到Internet后是如何訪問(wèn)到我們指定的服務(wù)器的呢?通信過(guò)程又是怎么控制的呢?那么首先看第一個(gè)概念,進(jìn)程通信。
進(jìn)程通信:這里的進(jìn)程通信包括兩種情況,一種是同一機(jī)器的不同進(jìn)程之間的通信,另一種是在同一網(wǎng)絡(luò)中(不同網(wǎng)絡(luò)通過(guò)路由進(jìn)行連接還是可以看成同一網(wǎng)絡(luò))的不同機(jī)器的不同進(jìn)程之間的通信。在同一臺(tái)機(jī)器中的進(jìn)程通信問(wèn)題,由于每個(gè)進(jìn)程都在自己的地址范圍內(nèi)運(yùn)行,為保證兩個(gè)相互通信的進(jìn)程之間既互不干擾又協(xié)調(diào)一致工作,操作系統(tǒng)為進(jìn)程通信提供了相應(yīng)設(shè)施,如管道(pipe)、命名管道(named pipe)和信號(hào)量(semaphore)等。各個(gè)進(jìn)程要進(jìn)行通信首先要解決進(jìn)程的標(biāo)識(shí)問(wèn)題,在同一機(jī)器中,可用process ID來(lái)唯一標(biāo)識(shí)每個(gè)單獨(dú)的進(jìn)程,我們可以在任務(wù)管理器中進(jìn)行查看,每個(gè)進(jìn)行都有自己唯一的標(biāo)志符。如果沒(méi)有看到的,可以在任務(wù)管理器中點(diǎn)擊“查看”,然后點(diǎn)擊“選擇列”,在出現(xiàn)的對(duì)話框中選中“PID(進(jìn)程標(biāo)志符)”這一欄,確定后我們就可以看到每個(gè)進(jìn)程的PID了,。而在網(wǎng)絡(luò)中的不同電腦要進(jìn)行通信,首先要經(jīng)過(guò)網(wǎng)絡(luò)間的協(xié)議轉(zhuǎn)換然后再尋址找到我們的目的機(jī)器,最后根據(jù)特定標(biāo)志符找到特定的進(jìn)程,于是我們的客戶端進(jìn)程就可以和服務(wù)器進(jìn)程進(jìn)行網(wǎng)間進(jìn)程通信了,在這一過(guò)程中扮演著重要角色的就是TCP/IP協(xié)議
TCP/IP協(xié)議:TCP/IP是一個(gè)協(xié)議簇,它包括網(wǎng)絡(luò)接口層,網(wǎng)絡(luò)層、傳輸層和應(yīng)用層,網(wǎng)絡(luò)層中有負(fù)責(zé)因特網(wǎng)地址(IP地址)與底層網(wǎng)絡(luò)地址之間進(jìn)行轉(zhuǎn)換的地址解析協(xié)議ARP和反向地址解析協(xié)議RARP。同時(shí)也包括對(duì)主機(jī)和網(wǎng)關(guān)進(jìn)行差錯(cuò)報(bào)告、控制和進(jìn)行請(qǐng)求/應(yīng)答的IGMP協(xié)議和網(wǎng)絡(luò)層的核心協(xié)議IP協(xié)議。在TCP/IP協(xié)議簇中的傳輸層中,提供了進(jìn)程間的通信的TCP和UDP協(xié)議,這兩個(gè)協(xié)議分別提供了了可靠的面向連接的傳輸服務(wù)和簡(jiǎn)單高效的無(wú)連接傳輸服務(wù),我們最需要了解的就是傳輸層中的這兩個(gè)協(xié)議。
IP地址:因特網(wǎng)的IP協(xié)議提供了一種整個(gè)互聯(lián)網(wǎng)中通用的地址格式,并在同一管理下進(jìn)行IP地址的分配并保證其唯一性,以確保每臺(tái)因特網(wǎng)主機(jī)(路由器)對(duì)應(yīng)一個(gè)IP地址。
端口:網(wǎng)絡(luò)中可以被命名和尋址的通信端口,是操作系統(tǒng)可分配的一種資源。按照OSI七層協(xié)議的描述,傳輸層與網(wǎng)絡(luò)層在功能上的最大區(qū)別是傳輸層提供進(jìn)程通信能力。從這個(gè)意義上講,網(wǎng)絡(luò)通信的最終地址就不僅僅是主機(jī)地址了,還包括可以描述進(jìn)程的某種標(biāo)識(shí)符。為此,TCP/IP協(xié)議提出了協(xié)議端口(protocol port,簡(jiǎn)稱端口)的概念,用于標(biāo)識(shí)通信的進(jìn)程。
端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū))。應(yīng)用程序(即進(jìn)程)通過(guò)系統(tǒng)調(diào)用與某端口建立連接(binding)后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進(jìn)程所接收,相應(yīng)進(jìn)程發(fā)給傳輸層的數(shù)據(jù)都通過(guò)該端口輸出。在TCP/IP協(xié)議的實(shí)現(xiàn)中,端口類似于一般的I/O操作,進(jìn)程獲取一個(gè)端口,相當(dāng)于獲取本地唯一的I/O文件,可以用一般的讀寫原語(yǔ)訪問(wèn)之,如我們通過(guò)指定端口讀取GPS信息等。
類似于文件描述符,每個(gè)端口都擁有一個(gè)叫端口號(hào)(port number)的整數(shù)型標(biāo)識(shí)符,用于區(qū)別不同端口。由于TCP/IP傳輸層的兩個(gè)協(xié)議TCP和UDP是完全獨(dú)立的兩個(gè)軟件模塊,因此各自的端口號(hào)也相互獨(dú)立,如TCP有一個(gè)255號(hào)端口,UDP也可以有一個(gè)255號(hào)端口,二者并不沖突。因此當(dāng)我們通過(guò)指定的IP地址和端口號(hào)就可以找到唯一標(biāo)志我們的進(jìn)程了。
在了解了上述基礎(chǔ)知識(shí)后,我們可以簡(jiǎn)單做個(gè)回顧,總結(jié)下整個(gè)連接的過(guò)程。本文介紹的客戶端與服務(wù)器端通信是典型的C/S模式,客戶端在請(qǐng)求服務(wù)器端提供特定服務(wù)后,服務(wù)器端接收請(qǐng)求并提供相應(yīng)服務(wù)。在TCP/IP網(wǎng)絡(luò)應(yīng)用中,C/S模式中服務(wù)器端是采取主動(dòng)的方式,首先啟動(dòng),并根據(jù)請(qǐng)求提供相應(yīng)服務(wù)。
服務(wù)器端:
1. 打開一通信通道并告知本地主機(jī),它愿意在某一公認(rèn)地址上接收客戶請(qǐng)求;
2. 等待客戶請(qǐng)求到達(dá)該端口;
3. 接收到服務(wù)請(qǐng)求,處理該請(qǐng)求并發(fā)送應(yīng)答信號(hào)
4. 返回第二步,等待另一客戶請(qǐng)求。
5. 關(guān)閉服務(wù)器
客戶端:
1. 打開一通信通道,并連接到服務(wù)器所在主機(jī)的特定端口;
2. 向服務(wù)器發(fā)服務(wù)請(qǐng)求報(bào)文,等待并接收應(yīng)答;繼續(xù)提出請(qǐng)求......
3. 請(qǐng)求結(jié)束后關(guān)閉通信通道并終止。
客戶端主界面如圖所示:
【代碼部分】
客戶端主要功能為建立服務(wù)器的連接,和服務(wù)器互相通信(發(fā)送數(shù)據(jù)和接收數(shù)據(jù)),其中用到的關(guān)鍵的核心類為CConnectionManager類和CTCPClient_CE類,而服務(wù)器端主要負(fù)責(zé)偵聽同時(shí)也發(fā)送數(shù)據(jù)給客戶端,用到的核心類為ConnectionManager,客戶端和服務(wù)器利用socket通信步驟如下:
第一步:實(shí)例化套接字。
用WINSOCK API方式如下(客戶端):
服務(wù)器端用.net平臺(tái)如下:
第二步,進(jìn)行偵聽。獲取數(shù)據(jù),發(fā)送數(shù)據(jù)。
客戶端發(fā)送數(shù)據(jù):
可以看出,雖然他們語(yǔ)法不相同,語(yǔ)義卻相同。在實(shí)例化一個(gè)套接字對(duì)象socket時(shí),我們都要指定協(xié)議簇,套接字類型(有流式套接字、數(shù)據(jù)報(bào)套接字和原始套接字等類型)和傳輸協(xié)議,成功獲取套接字后服務(wù)器端要與指定端口綁定(Bind),然后進(jìn)行監(jiān)聽(Listen),并調(diào)用accept ()方法。Accept()以同步方式從偵聽套接字的連接請(qǐng)求隊(duì)列中提取第一個(gè)掛起的連接請(qǐng)求,然后創(chuàng)建并返回新的 Socket,而客戶端完成套接字的實(shí)例化后,開始調(diào)用Select()函數(shù)判斷是否有讀事件發(fā)生,如果有則調(diào)用Recv()函數(shù)獲取從服務(wù)器端發(fā)來(lái)的數(shù)據(jù)或者調(diào)用Send()函數(shù)來(lái)向服務(wù)器發(fā)送數(shù)據(jù)。
客戶端主要函數(shù)為:
bool Open(CWnd * pWnd);
bool Connect();
bool SendData(const char * buf , int len);
bool Close();
服務(wù)器端主要函數(shù)為:
void StartToListen(object sender, DoWorkEventArgs e);
void CreateNewClientManager(Socket socket);
void StartReceive(object sender, DoWorkEventArgs e);
void SendCommandToClient(Command cmd);
三、引用(參考)文章
1. http://bbs.chinaunix.net/viewthread.php?tid=198859(socket編程原理-很不錯(cuò));
2.http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket_methods.aspx(msdn 開發(fā)中心socket部分)
3.實(shí)戰(zhàn)篇源代碼下載:客戶端 服務(wù)器端
聯(lián)系客服