小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

GPRS開發(fā)系列文章之實(shí)戰(zhàn)篇

 instl 2014-11-21

轉(zhuǎn)自:http://www.cnblogs.com/jsjkandy/archive/2008/08/12/1266345.html

 

一、前言

在前篇《GPRS開發(fā)系列文章之進(jìn)階篇》里,我主要詳細(xì)講解了客戶端進(jìn)行GPRS連接的常用API,并對GPRSdemo測試程序中的連接類ConnManager中的一些重要函數(shù)做了說明,最后稍微提及了下服務(wù)器端要用到的一些類庫。今天,在這篇實(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連接的問題,因此剩下的主要就是我們都比較熟悉的SOCKET編程了,由于客戶端和服務(wù)器端進(jìn)行SOCKET通信的原理相同,所以我將他們放到一起進(jìn)行講解,主要介紹SOCKET編程的一些原理及要點(diǎn),然后貼出部分比較重要的代碼供大家參考。
   
首先,介紹些要了解SOCKET編程的一些核心概念
   
我們知道在這種通信程序中,一般客戶端和服務(wù)器端是分開的(本機(jī)通信可以看作是一種特例),客戶端一旦和服務(wù)器端建立連接成功后就可以透明的傳輸數(shù)據(jù)和接收數(shù)據(jù)了。那么我們的程序在建立了GPRS連接到Internet后是如何訪問到我們指定的服務(wù)器的呢?通信過程又是怎么控制的呢?那么首先看第一個(gè)概念,進(jìn)程通信。
    
進(jìn)程通信:這里的進(jìn)程通信包括兩種情況,一種是同一機(jī)器的不同進(jìn)程之間的通信,另一種是在同一網(wǎng)絡(luò)中(不同網(wǎng)絡(luò)通過路由進(jìn)行連接還是可以看成同一網(wǎng)絡(luò))的不同機(jī)器的不同進(jìn)程之間的通信。在同一臺(tái)機(jī)器中的進(jìn)程通信問題,由于每個(gè)進(jìn)程都在自己的地址范圍內(nèi)運(yùn)行,為保證兩個(gè)相互通信的進(jìn)程之間既互不干擾又協(xié)調(diào)一致工作,操作系統(tǒng)為進(jìn)程通信提供了相應(yīng)設(shè)施,如管道(pipe)、命名管道(named pipe)和信號量(semaphore)等。各個(gè)進(jìn)程要進(jìn)行通信首先要解決進(jìn)程的標(biāo)識(shí)問題,在同一機(jī)器中,可用process ID來唯一標(biāo)識(shí)每個(gè)單獨(dú)的進(jìn)程,我們可以在任務(wù)管理器中進(jìn)行查看,每個(gè)進(jìn)行都有自己唯一的標(biāo)志符。如果沒有看到的,可以在任務(wù)管理器中點(diǎn)擊“查看”,然后點(diǎn)擊“選擇列”,在出現(xiàn)的對話框中選中“PID(進(jìn)程標(biāo)志符)”這一欄,確定后我們就可以看到每個(gè)進(jìn)程的PID了,。而在網(wǎng)絡(luò)中的不同電腦要進(jìn)行通信,首先要經(jīng)過網(wǎng)絡(luò)間的協(xié)議轉(zhuǎn)換然后再尋址找到我們的目的機(jī)器,最后根據(jù)特定標(biāo)志符找到特定的進(jìn)程,于是我們的客戶端進(jìn)程就可以和服務(wù)器進(jìn)程進(jìn)行網(wǎng)間進(jìn)程通信了,在這一過程中扮演著重要角色的就是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í)也包括對主機(jī)和網(wǎng)關(guān)進(jìn)行差錯(cuò)報(bào)告、控制和進(jìn)行請求/應(yīng)答的IGMP協(xié)議和網(wǎng)絡(luò)層的核心協(xié)議IP協(xié)議。在TCP/IP協(xié)議簇中的傳輸層中,提供了進(jìn)程間的通信的TCPUDP協(xié)議,這兩個(gè)協(xié)議分別提供了了可靠的面向連接的傳輸服務(wù)和簡單高效的無連接傳輸服務(wù),我們最需要了解的就是傳輸層中的這兩個(gè)協(xié)議。
    IP
地址:因特網(wǎng)的IP協(xié)議提供了一種整個(gè)互聯(lián)網(wǎng)中通用的地址格式,并在同一管理下進(jìn)行IP地址的分配并保證其唯一性,以確保每臺(tái)因特網(wǎng)主機(jī)(路由器)對應(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,簡稱端口)的概念,用于標(biāo)識(shí)通信的進(jìn)程。
    端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū))。應(yīng)用程序(即進(jìn)程)通過系統(tǒng)調(diào)用與某端口建立連接(binding)后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進(jìn)程所接收,相應(yīng)進(jìn)程發(fā)給傳輸層的數(shù)據(jù)都通過該端口輸出。在TCP/IP協(xié)議的實(shí)現(xiàn)中,端口類似于一般的I/O操作,進(jìn)程獲取一個(gè)端口,相當(dāng)于獲取本地唯一的I/O文件,可以用一般的讀寫原語訪問之,如我們通過指定端口讀取GPS信息等。 
    
類似于文件描述符,每個(gè)端口都擁有一個(gè)叫端口號(port number)的整數(shù)型標(biāo)識(shí)符,用于區(qū)別不同端口。由于TCP/IP傳輸層的兩個(gè)協(xié)議TCPUDP是完全獨(dú)立的兩個(gè)軟件模塊,因此各自的端口號也相互獨(dú)立,如TCP有一個(gè)255號端口,UDP也可以有一個(gè)255號端口,二者并不沖突。因此當(dāng)我們通過指定的IP地址和端口號就可以找到唯一標(biāo)志我們的進(jìn)程了。
    
在了解了上述基礎(chǔ)知識(shí)后,我們可以簡單做個(gè)回顧,總結(jié)下整個(gè)連接的過程。本文介紹的客戶端與服務(wù)器端通信是典型的C/S模式,客戶端在請求服務(wù)器端提供特定服務(wù)后,服務(wù)器端接收請求并提供相應(yīng)服務(wù)。在TCP/IP網(wǎng)絡(luò)應(yīng)用中,C/S模式中服務(wù)器端是采取主動(dòng)的方式,首先啟動(dòng),并根據(jù)請求提供相應(yīng)服務(wù)。
     
服務(wù)器端:
   1. 
打開一通信通道并告知本地主機(jī),它愿意在某一公認(rèn)地址上接收客戶請求; 
   2. 
等待客戶請求到達(dá)該端口; 
   3. 
接收到服務(wù)請求,處理該請求并發(fā)送應(yīng)答信號
   4. 
返回第二步,等待另一客戶請求。 
   5. 
關(guān)閉服務(wù)器
    
客戶端: 
   1. 
打開一通信通道,并連接到服務(wù)器所在主機(jī)的特定端口; 
   2. 
向服務(wù)器發(fā)服務(wù)請求報(bào)文,等待并接收應(yīng)答;繼續(xù)提出請求...... 
   3. 
請求結(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方式如下(客戶端):

bool CTCPClient_CE::Connect()
{
   struct sockaddr_in addr;
   int err;

   addr.sin_family = AF_INET;
   addr.sin_port = htons(m_port);
   //此處要將雙字節(jié)轉(zhuǎn)換成單字節(jié)
   char ansiRemoteHost[255];
    ZeroMemory(ansiRemoteHost,255);
    WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,m_remoteHost,wcslen(m_remoteHost)
        ,ansiRemoteHost,wcslen(m_remoteHost),NULL,NULL);

   addr.sin_addr.s_addr=inet_addr(ansiRemoteHost);

   //創(chuàng)建TCP套接字 
   m_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
   if (m_socket == INVALID_SOCKET)
   {
       return FALSE;
   }
   //此時(shí)采用同步連接方式,connect直接返回成功或是失敗
   err = connect(m_socket,(struct sockaddr *)&addr,sizeof(addr));
   if (err == SOCKET_ERROR) 
   {
       return FALSE;
   }
   //設(shè)置通訊模式為異步模式
   DWORD ul= 1;
   ioctlsocket(m_socket,FIONBIO,&ul);
   return TRUE;
}


服務(wù)器端用.net平臺(tái)如下:

private void StartToListen(object sender, DoWorkEventArgs e)
        {
            try
            {
                this.listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this.listenerSocket.Bind(new IPEndPoint(this.serverIP, this.serverPort));
                this.listenerSocket.Listen(200);
                while (bListen)
                    this.CreateNewClientManager(this.listenerSocket.Accept());
            }
            catch(SocketException ex)
            {
                if (ex.ErrorCode == 10004)
                    return;
                else
                {
                    throw ex;
                }
            }
        }


第二步,進(jìn)行偵聽。獲取數(shù)據(jù),發(fā)送數(shù)據(jù)。
客戶端發(fā)送數(shù)據(jù):

bool CTCPClient_CE::SendData(const char * buf , int len)
{
    int nBytes = 0;
    int nSendBytes=0;
            
    while (nSendBytes < len)
    {
        nBytes = send(m_socket,buf+nSendBytes,len-nSendBytes,0);
        if (nBytes==SOCKET_ERROR )
        {
            int iErrorCode = WSAGetLastError();
            //觸發(fā)socket的Error事件
            OnError(m_pOwnerWnd,iErrorCode);
            //觸發(fā)與服務(wù)器端斷開連接事件
            OnDisConnect(m_pOwnerWnd);
            //關(guān)閉socket
            Close();
            return FALSE;
        }

        nSendBytes = nSendBytes + nBytes;
        
        if (nSendBytes < len)
        {
            Sleep(1000);
        }
    } 
    return TRUE; 
}

              服務(wù)器端發(fā)送數(shù)據(jù):

 

private bool SendCommandToClient(Command cmd)
        {
            try
            {
                semaphor.WaitOne();
                string strSentInfo = string.Empty;
                strSentInfo = string.Format("發(fā)送者:{0}{1}內(nèi)容:{2}", cmd.SenderName, Environment.NewLine, cmd.MetaData);

                byte[] buffer = new byte[256];
                buffer = System.Text.Encoding.Default.GetBytes(strSentInfo);
                this.networkStream.Write(buffer, 0, buffer.GetLength(0));
                this.networkStream.Flush();               
               
                semaphor.Release();
                return true;
            }
            catch
            {
                semaphor.Release();
                return false;
            }
        }



可以看出,雖然他們語法不相同,語義卻相同。在實(shí)例化一個(gè)套接字對象socket時(shí),我們都要指定協(xié)議簇,套接字類型(有流式套接字、數(shù)據(jù)報(bào)套接字和原始套接字等類型)和傳輸協(xié)議,成功獲取套接字后服務(wù)器端要與指定端口綁定(Bind),然后進(jìn)行監(jiān)聽(Listen),并調(diào)用accept ()方法。Accept()以同步方式從偵聽套接字的連接請求隊(duì)列中提取第一個(gè)掛起的連接請求,然后創(chuàng)建并返回新的 Socket,而客戶端完成套接字的實(shí)例化后,開始調(diào)用Select()函數(shù)判斷是否有讀事件發(fā)生,如果有則調(diào)用Recv()函數(shù)獲取從服務(wù)器端發(fā)來的數(shù)據(jù)或者調(diào)用Send()函數(shù)來向服務(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./viewthread.php?tid=198859socket編程原理-很不錯(cuò));

2.http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket_methods.aspx(msdn 開發(fā)中心socket部分)

3.實(shí)戰(zhàn)篇源代碼下載:客戶端 服務(wù)器端

 

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多