• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            2013年6月1日


            本文主要討論如何將一個win32項目,移植到android上面,網(wǎng)上很多文章說的不一樣,因為cocos2d-x的android項目配置方法好像修改過幾次(好像?我沒用過老版的),本文提供一種簡單的方法。

            第零步:配置android工程生成器
            #1用ue之類的編輯器打開cocos2d-x目錄下的create-android-project.bat

            #2編輯這三個變量

            意思太明白了,就不解釋了


            第一步:建立android工程

            #1運行cocos2d-x目錄下的create-android-project.bat

            #2然后首先輸入包名和項目名。
            注意有坑!這會刪除和覆蓋你在cocos2d-x安裝目錄下同名文件夾,如果你建立了一個同名項目(有的話一般是win32的)的話。
            本地圖片,請重新上傳
            #3然后輸入支持系統(tǒng)版本,android是向下兼容的。我這里寫5,也就是android2.1-update1
            本地圖片,請重新上傳
            然后項目就建立好了,你會發(fā)現(xiàn)在cocos2d-x安裝目錄下,多了個Test文件夾

            第二步:導入代碼和資源

            #1打開Test

            本地圖片,請重新上傳
            #2我們看到有三個文件夾,其中android就是android項目文件夾
            把你win32項目里的Classes拷貝到當前Classes下(注意刪除Classes里面的多余的cpp和h,就是項目自動生成的那些)
            把你win32項目里的Resources拷貝到當前Resources下(注意刪除Classes里面的多余資源,就是項目自動生成的那些)

            第三步:編輯mk

            #1注意有坑!android項目里有一大堆mk,而且不同的教程說的mk位置還不一樣!你編輯錯誤了,是無效的(跟你實際使用的mk也有關(guān)系)。在這個例子中,我們使用的是Classes下的mk

            本地圖片,請重新上傳
            #2要修改的地方如下
            LOCAL_SRC_FILES:在這里加入你Classes下的cpp文件
            LOCAL_C_INCLUDES:在這里添加你使用的庫的h文件,如果有的話
            LOCAL_LDLIBS:在這里添加你使用的庫的lib文件,如果有的話

            這里我使用的是,之前的“是男人就堅持20秒”那個例子的代碼

            注意,第三方庫和額外的庫都是要自己添加的。默認生成的mk里沒那么全。

            第四步:運行build_native.sh腳本,編輯so庫

            #1運行你的cygwin安裝目錄下的Cygwin.bat

            #2進入當前Test\android路徑下
            本地圖片,請重新上傳
            注意cygwin下的命令寫法,和win下不同
            #3運行build_native.sh腳本
            頭一次編譯會長些,當然圖省事你也可以把自己及其他的android項目里面的so復制進來(應該可以把,哈哈哈)
            編譯完然后你會看到,在android文件夾下面生成了一大堆東西。

            第五步:導入到eclipse

            #1注意有坑!使用新建android項目下的Create project from existing source,如果你使用的是“導入”有可能無法識別。
            #2導入成功,然后就是編譯執(zhí)行了。cocos2d-x作者推薦用sh編譯so,再用eclipse編譯成apk。
            本地圖片,請重新上傳

            其他注意事項:


            #1.win32項目對資源文件大小寫不敏感,android敏感,所以如果出錯了,把給你提供資源的人給打一頓
            #2.可以直接把win32項目里的工程文件,以及win32文件夾復制進來,項目就可以和VS共用了,當然cocos2d-x也是這么干的
            #3.vs用的是gb碼,android用的是utf-8,解決方法在vs下用iconv,另外最好全都轉(zhuǎn)成utf-8,網(wǎng)上有很多編碼批量轉(zhuǎn)換工具
            #4.android的橫豎屏問題錯亂,在android下把
            pDirector->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);
            這句用條件編譯宏給關(guān)掉

             

             

             

            另外,關(guān)于cocos2d-x在android上添加第三方庫的問題,6群網(wǎng)友塞風朔雪寫了一個文檔,很有參考價值。我已把它上傳到附件中。


            posted @ 2013-06-01 10:04 鄭興鋒 閱讀(1162) | 評論 (0)編輯 收藏

            環(huán)境winxp + android sdk + ndk r8b+ cygwin 1.7.16-1 + cocos2d-1.0.1-x-0.12.0

            1.下載android sdk、ndk、cygwin

            http://dl.google.com/android/android-sdk_r20.0.1-windows.zip

            http://dl.google.com/android/ndk/android-ndk-r8b-windows.zip

            http://cygwin.com/setup.exe

            2.android sdk的安裝就不多說了,網(wǎng)上多的是。

            將ndk解壓到不含空格的目錄下,下文用<ndk_dir>來表示解壓后的ndk根目錄。

            下載好cygwin后,運行setup.exe。需要安裝的組件有:

            autoconf automake binutils gcc-core gcc-g++ gcc4-core gcc4-g++ gdb pcre pcre-devel gawk make

            可以在上方search處進行查找安裝,下文用<cyg_dir>表示cygwin的安裝目錄。

            3.cygwin安裝好后,在windows下編輯<cyg_dir>\home\Administrator\.bash_profile文件

            在文件最后添加如下內(nèi)容

             

            1. NDK=/cygdrive/d/<這里是ndk路徑>/android-ndk-r8b  
            2. export NDK  
            NDK=/cygdrive/d/<這里是ndk路徑>/android-ndk-r8bexport NDK
            編輯cocos2dx目錄下的create-android-project.bat文件,分別修改如下幾個變量的值

             

             

            1. set _CYGBIN=C:\cygwin\bin  
            2. set _ANDROIDTOOLS=C:\android-sdk-windows\tools  
            3. set _NDKROOT=D:\Tools\Developer\Android\android-ndk-r8b  
            set _CYGBIN=C:\cygwin\binset _ANDROIDTOOLS=C:\android-sdk-windows\toolsset _NDKROOT=D:\Tools\Developer\Android\android-ndk-r8b
            上面是我的系統(tǒng)中使用的路徑,同學們需要根據(jù)自己的實際情況進行修改。

             

            這樣,環(huán)境基本上就搭建好了,下面需要建一個hello world工程來驗證一下環(huán)境是否可用。

            1.運行cocos2dx目錄下的create-android-project.bat文件,根據(jù)提示輸入包名(例如:cn.wey.android)、項目名稱(例如:hello2dx)、所使用的android sdk版本。

            2.運行cygwin,在命令窗口中進入剛剛新建的hello2dx目錄下的android目錄,運行命令

             

            1. ./build_native.sh  
            ./build_native.sh
            對工程進行編譯,直到最后正常結(jié)束。

             

            3.打開eclipse,導入hello2dx項目,編譯并運行。即可看到經(jīng)典的cocos2dx的hello world界面

            posted @ 2013-06-01 10:02 鄭興鋒 閱讀(1884) | 評論 (0)編輯 收藏

            2011年11月21日

              http://blog.csdn.net/xiaominghimi?viewmode=contents 
              http://www.cnblogs.com/andyque/tag/iphone

            posted @ 2011-11-21 10:39 鄭興鋒 閱讀(318) | 評論 (0)編輯 收藏

            2011年11月20日

              最近在用VC++開發(fā)一個小工具,平時用慣了.NET,用起VC++最郁悶的就是字符串處理。當然最最讓人難于琢磨的就是字符集,編碼之間的轉(zhuǎn)換。通過這幾天的研究,終于明白了Unicode和UTF-8之間編碼的區(qū)別。Unicode是一個字符集,而UTF-8是Unicode的其中一種,Unicode是定長的都為雙字節(jié),而UTF-8是可變的,對于漢字來說Unicode占有的字節(jié)比UTF-8占用的字節(jié)少1個字節(jié)。Unicode為雙字節(jié),而UTF-8中漢字占三個字節(jié)。
                                    網(wǎng)魂小兵 http://xdotnet.cnblogs.com
                UTF-8編碼字符理論上可以最多到6個字節(jié)長,然而16位BMP(Basic Multilingual Plane)字符最多只用到3字節(jié)長。下面看一下UTF-8編碼表:

                    U-00000000 - U-0000007F: 0xxxxxxx
                    U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
                    U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
                    U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
                    U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
                    U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

                xxx 的位置由字符編碼數(shù)的二進制表示的位填入, 越靠右的 x 具有越少的特殊意義,只用最短的那個足夠表達一個字符編碼數(shù)的多字節(jié)串。 注意在多字節(jié)串中, 第一個字節(jié)的開頭"1"的數(shù)目就是整個串中字節(jié)的數(shù)目。而第一行中以0開頭,是為了兼容ASCII編碼,為一個字節(jié),第二行就為雙字節(jié)字符串,第三行為3字節(jié),如漢字就屬于這種,以此類推。(個人認為:其實我們可以簡單的把前面的1的個數(shù)看成字節(jié)數(shù))
                                     網(wǎng)魂小兵 http://xdotnet.cnblogs.com
                為了要將Unicode轉(zhuǎn)換為UTF-8,當然要知道他們的區(qū)別到底在什么地方。下面來看一下,在Unicode中的編碼是怎樣轉(zhuǎn)換成UTF-8的,在UTF-8中,如果一個字符的字節(jié)小于0x80(128)則為ASCII字符,占一個字節(jié),可以不用轉(zhuǎn)換,因為UTF-8兼容ASCII編碼。假如在Unicode中漢字“你”的編碼為“u4F60”,把它轉(zhuǎn)換為二進制為100111101100000,然后按照UTF-8的方法進行轉(zhuǎn)換。可以將Unicode二進制從地位往高位取出二進制數(shù)字,每次取6位,如上述的二進制就可以分別取出為如下所示的格式,前面按格式填補,不足8位用0填補。

                  
                       unicode:  100111101100000                  4F60

                       utf-8:    11100100,10111101,10100000       E4BDA0


                從上面就可以很直觀的看出Unicode到UTF-8之間的轉(zhuǎn)換,當然知道了UTF-8的格式后,就可以進行逆運算,就是按照格式把它在二進制中的相應位置上取出,然后在轉(zhuǎn)換就是所得到的Unicode字符了(這個運算可以通過“位移”來完成)。
                                  網(wǎng)魂小兵 http://xdotnet.cnblogs.com
                如上述的“你”的轉(zhuǎn)換,由于其值大于0x800小于0x10000,因此可以判斷為三字節(jié)存儲,則最高位需要向右移“12”位再根據(jù)三字節(jié)格式的最高位為11100000(0xE0)求或(|)就可以得到最高位的值了。同理第二位則是右移“6”位,則還剩下最高位和第二位的二進制值,可以通過與111111(0x3F)求按位于(&)操作,再和11000000(0x80)求或(|)。第三位就不用移位了,只要直接取最后六位(與111111(ox3F)取&),在與11000000(0x80)求或(|)。OK了,轉(zhuǎn)換成功!在VC++中的代碼如下所示(Unicode到UTF-8的轉(zhuǎn)換)。

                    1 const wchar_t pUnicode = L"你";
                    2 char utf8[3+1];
                    3 memset(utf8,0,4);
                    4 utf8[0] = 0xE0|(pUnicode>>12);
                    5 utf8[1] = 0x80|((pUnicode>>6)&0x3F);
                    6 utf8[2] = 0x80|(pUnicode&0x3F);
                    7 utf8[3] = "\0";
                    8 //char[4]就是UTF-8的字符“你”了。

                當然在UTF-8到Unicode的轉(zhuǎn)換也是通過移位等來完成的,就是把UTF-8那些格式相應的位置的二進制數(shù)給揪出來。在上述例子中“你”為三個字節(jié),因此要每個字節(jié)進行處理,有高位到低位進行處理。在UTF-8中“你”為11100100,10111101,10100000。從高位起即第一個字節(jié)11100100就是把其中的"0100"給取出來,這個很簡單只要和11111(0x1F)取與(&),由三字節(jié)可以得知最到位肯定位于12位之前,因為每次取六位。所以還要將得到的結(jié)果左移12位,最高位也就這樣完成了0100,000000,000000。而第二位則是要把“111101”給取出來,則只需將第二字節(jié)10111101和111111(0x3F)取與(&)。在將所得到的結(jié)果左移6位與最高字節(jié)所得的結(jié)果取或(|),第二位就這樣完成了,得到的結(jié)果為0100,111101,000000。以此類推最后一位直接與111111(0x3F)取與(&),再與前面所得的結(jié)果取或(|)即可得到結(jié)果0100,111101,100000。OK,轉(zhuǎn)換成功!在VC++中的代碼如下所示(UTF-8到Unicode的轉(zhuǎn)換)。

                1 //UTF-8格式的字符串
                2 const char* utf8 = "你";
                3 wchar_t unicode;
                4 unicode = (utf8[0] & 0x1F) << 12;
                5 unicode |= (utf8[1] & 0x3F) << 6;
                6 unicode |= (utf8[2] & 0x3F);
                7 //unicode is ok!
                                        網(wǎng)魂小兵 http://xdotnet.cnblogs.com
                當然在編程過程中不可能只轉(zhuǎn)換一個字符,這里需要注意的是字符的長度一定要算清楚,不然會帶來...以上就是我這幾天研究的結(jié)果,至于Unicode的轉(zhuǎn)換為GB2312在MFC中Windows有自帶的API(WideCharToMultiByte)可以轉(zhuǎn)換。這樣也就能夠?qū)TF-8格式轉(zhuǎn)換為GB2312了,這里就不再贅述,如果大家有更好的方法希望指教。

            posted @ 2011-11-20 13:52 鄭興鋒 閱讀(360) | 評論 (0)編輯 收藏

            2011年11月16日

            1.closesocket(一般不會立即關(guān)閉而經(jīng)歷TIME_WAIT的過程)后想繼續(xù)重用該socket:
            BOOL bReuseaddr=TRUE;
            setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
            2. 如果要已經(jīng)處于連接狀態(tài)的soket在調(diào)用closesocket后強制關(guān)閉,不經(jīng)歷
            TIME_WAIT的過程:
            BOOL bDontLinger = FALSE;
            setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
            3.在send(),recv()過程中有時由于網(wǎng)絡(luò)狀況等原因,發(fā)收不能預期進行,而設(shè)置收發(fā)時限:
            int nNetTimeout=1000;//1秒
            //發(fā)送時限
            setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
            //接收時限
            setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));
            4.在send()的時候,返回的是實際發(fā)送出去的字節(jié)(同步)或發(fā)送到socket緩沖區(qū)的字節(jié)
            (異步);系統(tǒng)默認的狀態(tài)發(fā)送和接收一次為8688字節(jié)(約為8.5K);在實際的過程中發(fā)送數(shù)據(jù)
            和接收數(shù)據(jù)量比較大,可以設(shè)置socket緩沖區(qū),而避免了send(),recv()不斷的循環(huán)收發(fā):
            // 接收緩沖區(qū)
            int nRecvBuf=32*1024;//設(shè)置為32K
            setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
            //發(fā)送緩沖區(qū)
            int nSendBuf=32*1024;//設(shè)置為32K
            setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
            5. 如果在發(fā)送數(shù)據(jù)的時,希望不經(jīng)歷由系統(tǒng)緩沖區(qū)到socket緩沖區(qū)的拷貝而影響
            程序的性能:
            int nZero=0;
            setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));
            6.同上在recv()完成上述功能(默認情況是將socket緩沖區(qū)的內(nèi)容拷貝到系統(tǒng)緩沖區(qū)):
            int nZero=0;
            setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));
            7.一般在發(fā)送UDP數(shù)據(jù)報的時候,希望該socket發(fā)送的數(shù)據(jù)具有廣播特性:
            BOOL bBroadcast=TRUE;
            setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));
            8.在client連接服務器過程中,如果處于非阻塞模式下的socket在connect()的過程中可
            以設(shè)置connect()延時,直到accpet()被呼叫(本函數(shù)設(shè)置只有在非阻塞的過程中有顯著的
            作用,在阻塞的函數(shù)調(diào)用中作用不大)
            BOOL bConditionalAccept=TRUE;
            setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));
            9.如果在發(fā)送數(shù)據(jù)的過程中(send()沒有完成,還有數(shù)據(jù)沒發(fā)送)而調(diào)用了closesocket(),以前我們
            一般采取的措施是"從容關(guān)閉"shutdown(s,SD_BOTH),但是數(shù)據(jù)是肯定丟失了,如何設(shè)置讓程序滿足具體
            應用的要求(即讓沒發(fā)完的數(shù)據(jù)發(fā)送出去后在關(guān)閉socket)?
            struct linger {
            u_short l_onoff;
            u_short l_linger;
            };
            linger m_sLinger;
            m_sLinger.l_onoff=1;//(在closesocket()調(diào)用,但是還有數(shù)據(jù)沒發(fā)送完畢的時候容許逗留)
            // 如果m_sLinger.l_onoff=0;則功能和2.)作用相同;
            m_sLinger.l_linger=5;//(容許逗留的時間為5秒)
            setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));
             
            設(shè)置套接口的選項。
               #include <winsock.h>
               int PASCAL FAR setsockopt( SOCKET s, int level, int optname,
               const char FAR* optval, int optlen);
               s:標識一個套接口的描述字。
               level:選項定義的層次;目前僅支持SOL_SOCKET和IPPROTO_TCP層次。
               optname:需設(shè)置的選項。
               optval:指針,指向存放選項值的緩沖區(qū)。
               optlen:optval緩沖區(qū)的長度。
            注釋:
            setsockopt()函數(shù)用于任意類型、任意狀態(tài)套接口的設(shè)置選項值。盡管在不同協(xié)議層上存在選項,但本函數(shù)僅定義了最高的“套接口”層次上的選項。選項影響套接口的操作,諸如加急數(shù)據(jù)是否在普通數(shù)據(jù)流中接收,廣播數(shù)據(jù)是否可以從套接口發(fā)送等等。
               有兩種套接口的選項:一種是布爾型選項,允許或禁止一種特性;另一種是整形或結(jié)構(gòu)選項。允許一個布爾型選項,則將optval指向非零整形數(shù);禁止一個選項optval指向一個等于零的整形數(shù)。對于布爾型選項,optlen應等于sizeof(int);對其他選項,optval指向包含所需選項的整形數(shù)或結(jié)構(gòu),而optlen則為整形數(shù)或結(jié)構(gòu)的長度。SO_LINGER選項用于控制下述情況的行動:套接口上有排隊的待發(fā)送數(shù)據(jù),且 closesocket()調(diào)用已執(zhí)行。參見closesocket()函數(shù)中關(guān)于SO_LINGER選項對closesocket()語義的影響。應用程序通過創(chuàng)建一個linger結(jié)構(gòu)來設(shè)置相應的操作特性:
               struct linger {
            int l_onoff;
            int l_linger;
               };
               為了允許SO_LINGER,應用程序應將l_onoff設(shè)為非零,將l_linger設(shè)為零或需要的超時值(以秒為單位),然后調(diào)用setsockopt()。為了允許SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff應設(shè)為零,然后調(diào)用setsockopt()。
               缺省條件下,一個套接口不能與一個已在使用中的本地地址捆綁(參見bind())。但有時會需要“重用”地址。因為每一個連接都由本地地址和遠端地址的組合唯一確定,所以只要遠端地址不同,兩個套接口與一個地址捆綁并無大礙。為了通知WINDOWS套接口實現(xiàn)不要因為一個地址已被一個套接口使用就不讓它與另一個套接口捆綁,應用程序可在bind()調(diào)用前先設(shè)置SO_REUSEADDR選項。請注意僅在bind()調(diào)用時該選項才被解釋;故此無需(但也無害)將一個不會共用地址的套接口設(shè)置該選項,或者在bind()對這個或其他套接口無影響情況下設(shè)置或清除這一選項。
               一個應用程序可以通過打開SO_KEEPALIVE選項,使得WINDOWS套接口實現(xiàn)在TCP連接情況下允許使用“保持活動”包。一個WINDOWS套接口實現(xiàn)并不是必需支持“保持活動”,但是如果支持的話,具體的語義將與實現(xiàn)有關(guān),應遵守RFC1122“Internet主機要求-通訊層”中第 4.2.3.6節(jié)的規(guī)范。如果有關(guān)連接由于“保持活動”而失效,則進行中的任何對該套接口的調(diào)用都將以WSAENETRESET錯誤返回,后續(xù)的任何調(diào)用將以WSAENOTCONN錯誤返回。
               TCP_NODELAY選項禁止Nagle算法。Nagle算法通過將未確認的數(shù)據(jù)存入緩沖區(qū)直到蓄足一個包一起發(fā)送的方法,來減少主機發(fā)送的零碎小數(shù)據(jù)包的數(shù)目。但對于某些應用來說,這種算法將降低系統(tǒng)性能。所以TCP_NODELAY可用來將此算法關(guān)閉。應用程序編寫者只有在確切了解它的效果并確實需要的情況下,才設(shè)置TCP_NODELAY選項,因為設(shè)置后對網(wǎng)絡(luò)性能有明顯的負面影響。TCP_NODELAY是唯一使用IPPROTO_TCP層的選項,其他所有選項都使用SOL_SOCKET層。
               如果設(shè)置了SO_DEBUG選項,WINDOWS套接口供應商被鼓勵(但不是必需)提供輸出相應的調(diào)試信息。但產(chǎn)生調(diào)試信息的機制以及調(diào)試信息的形式已超出本規(guī)范的討論范圍。
            setsockopt()支持下列選項。其中“類型”表明optval所指數(shù)據(jù)的類型。
            選項        類型   意義
            SO_BROADCAST BOOL 允許套接口傳送廣播信息。
            SO_DEBUG BOOL 記錄調(diào)試信息。
            SO_DONTLINER BOOL 不要因為數(shù)據(jù)未發(fā)送就阻塞關(guān)閉操作。設(shè)置本選項相當于將SO_LINGER的l_onoff元素置為零。
            SO_DONTROUTE BOOL 禁止選徑;直接傳送。
            SO_KEEPALIVE BOOL 發(fā)送“保持活動”包。
            SO_LINGER struct linger FAR*   如關(guān)閉時有未發(fā)送數(shù)據(jù),則逗留。
            SO_OOBINLINE BOOL 在常規(guī)數(shù)據(jù)流中接收帶外數(shù)據(jù)。
            SO_RCVBUF int 為接收確定緩沖區(qū)大小。
            SO_REUSEADDR BOOL 允許套接口和一個已在使用中的地址捆綁(參見bind())。
            SO_SNDBUF int 指定發(fā)送緩沖區(qū)大小。
            TCP_NODELAY BOOL 禁止發(fā)送合并的Nagle算法。
            setsockopt()不支持的BSD選項有:
            選項名    類型 意義
            SO_ACCEPTCONN BOOL 套接口在監(jiān)聽。
            SO_ERROR int 獲取錯誤狀態(tài)并清除。
            SO_RCVLOWAT int 接收低級水印。
            SO_RCVTIMEO int 接收超時。
            SO_SNDLOWAT int 發(fā)送低級水印。
            SO_SNDTIMEO int 發(fā)送超時。
            SO_TYPE     int 套接口類型。
            IP_OPTIONS    在IP頭中設(shè)置選項。
            返回值:
               若無錯誤發(fā)生,setsockopt()返回0。否則的話,返回SOCKET_ERROR錯誤,應用程序可通過WSAGetLastError()獲取相應錯誤代碼。
            錯誤代碼:
               WSANOTINITIALISED:在使用此API之前應首先成功地調(diào)用WSAStartup()。
               WSAENETDOWN:WINDOWS套接口實現(xiàn)檢測到網(wǎng)絡(luò)子系統(tǒng)失效。
               WSAEFAULT:optval不是進程地址空間中的一個有效部分。
               WSAEINPROGRESS:一個阻塞的WINDOWS套接口調(diào)用正在運行中。
               WSAEINVAL:level值非法,或optval中的信息非法。
               WSAENETRESET:當SO_KEEPALIVE設(shè)置后連接超時。
               WSAENOPROTOOPT:未知或不支持選項。其中,SOCK_STREAM類型的套接口不支持SO_BROADCAST選項,SOCK_DGRAM 類型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE選項。
               WSAENOTCONN:當設(shè)置SO_KEEPALIVE后連接被復位。
               WSAENOTSOCK:描述字不是一個套接口。

            posted @ 2011-11-16 20:57 鄭興鋒 閱讀(414) | 評論 (0)編輯 收藏

            2011年3月22日

                   關(guān)于網(wǎng)游服務器的分類和布局的問題是在網(wǎng)絡(luò)游戲中比較關(guān)鍵的部分, 首先我們討論網(wǎng)絡(luò)游戲服務器的分類問題: 一般情況下游戲服務器分為: 登陸服務器, 網(wǎng)關(guān)服務器, 游戲服務器和數(shù)據(jù)庫服務器四類, 有的服務器可能是多臺并存的,如游戲服務器, 其中游戲服務器包括: 地圖服務器和邏輯服務器.
            下面這個圖描述了網(wǎng)關(guān)服務器和其他服務器之間的關(guān)系:
                 
            網(wǎng)關(guān)服務器的功能是將: 游戲服務器, 客戶端和數(shù)據(jù)庫服務器之間的消息進行轉(zhuǎn)發(fā), 所以它負責管理客戶端的連接和服務端的連接, 并轉(zhuǎn)發(fā)他們之間的消息, 同時他還連接到數(shù)據(jù)庫服務器上,等需要數(shù)據(jù)讀取是對數(shù)據(jù)庫進行操作,并轉(zhuǎn)發(fā)給相應的請求者.  (其實在對數(shù)據(jù)庫的連接這個問題上,有游戲服務器來連接的,網(wǎng)關(guān)服務只負責轉(zhuǎn)發(fā)和簡單的邏輯處理)。
                我們現(xiàn)在來討論下網(wǎng)關(guān)服務器是怎么實現(xiàn)的:網(wǎng)關(guān)服務器要管理兩個事情,一個是游戲服務器的連接,另一個玩家的連接。其實對于網(wǎng)關(guān)服務器而言,這兩個種類型的連接差別,并不是很大,可以用一個網(wǎng)路模型來處理收到的消息,并將其轉(zhuǎn)發(fā)給相應的接受者。
                游戲服務器中的地圖服務器:地圖服務器相對于網(wǎng)關(guān)服務器而言,它更像是一個客戶端,它在收到消息的時候處理消息,并將消息結(jié)果返回給網(wǎng)關(guān),網(wǎng)關(guān)把消息轉(zhuǎn)給相應的連接。只是說因為地圖服務器相對來說是處理那些長時間連接,需要不斷處理的邏輯的,如用戶的移動,狀態(tài),補給等,需要實時的消息處理。
                邏輯服務器的功能是實現(xiàn)如打怪,組隊等一些,不經(jīng)常交互的邏輯的。其實對于一組游戲服務器而言,大量的聊天信息也是很消耗系統(tǒng)資源的,有時候要單獨建立一個聊天服務器。
               數(shù)據(jù)庫服務器基本上是存放數(shù)據(jù)庫的,游戲服務器,邏輯服務器,在需要是讀取數(shù)據(jù),進行邏輯處理。

               
              














             

            posted @ 2011-03-22 17:38 鄭興鋒 閱讀(705) | 評論 (0)編輯 收藏

            2011年2月15日

            Windows系統(tǒng)編程之進程間通信
            北極星2003 當前離線


            Windows 的IPC(進程間通信)機制主要是異步管道和命名管道。(至于其他的IPC方式,例如內(nèi)存映射、郵槽等這里就不介紹了)
            管道(pipe)是用于進程間通信的共享內(nèi)存區(qū)域。創(chuàng)建管道的進程稱為管道服務器,而連接到這個管道的進程稱為管道客戶端。一個進程向管道寫入信息,而另外一個進程從管道讀取信息。
            異步管道是基于字符和半雙工的(即單向),一般用于程序輸入輸出的重定向;命名管道則強大地多,它們是面向消息和全雙工的,同時還允許網(wǎng)絡(luò)通信,用于創(chuàng)建客戶端/服務器系統(tǒng)。
            一、異步管道(實現(xiàn)比較簡單,直接通過實例來講解)
            實驗目標:當前有sample.cpp, sample.exe, sample.in這三個文件,sample.exe為sample.cpp的執(zhí)行程序,sample.cpp只是一個簡單的程序示例(簡單求和),如下:
            代碼:
            #include <iostream.h>
            int main()
            {
              int a, b ;
              while ( cin >> a >> b && ( a || b ) )
                cout << a + b << endl ;
              return 0;
            }
            
            Sample.in文件是輸入文件,內(nèi)容:
            32 433
            542 657
            0 0
            要求根據(jù)sample.exe和它的輸入數(shù)據(jù),把輸出數(shù)據(jù)重定向到sample.out
            流程分析:實際這個實驗中包含兩個部分,把輸入數(shù)據(jù)重定向到sample.exe 和把輸出數(shù)據(jù)重定向到sample.out。在命令行下可以很簡單的實現(xiàn)這個功能“sample <sample.in >sample.out”,這個命令也是利用管道特性實現(xiàn)的,現(xiàn)在我們就根據(jù)異步管道的實現(xiàn)原理自己來實現(xiàn)這個功能。
            管道是基于半雙工(單向)的,這里有兩個重定向的過程,顯然需要創(chuàng)建兩個管道,下面給出流程圖:
             
            異步管道實現(xiàn)的流程圖說明:
            1)。父進程是我們需要實現(xiàn)的,其中需要創(chuàng)建管道A,管道B,和子進程,整個實現(xiàn)流程分為4個操作。
            2)。管道A:輸入管道
            3)。管道B:輸出管道
            4)。操作A:把輸入文件sample.in的數(shù)據(jù)寫入輸入管道(管道A)
            5)。操作B:子進程從輸入管道中讀取數(shù)據(jù),作為該進程的加工原料。通常,程序的輸入數(shù)據(jù)由標準的輸入設(shè)備輸入,這里實現(xiàn)輸入重定向,即把輸入管道作為輸入設(shè)備。
            6)。操作C:子進程把加工后的成品(輸出數(shù)據(jù))輸出到輸出管道。通常,程序的輸出數(shù)據(jù)會輸出到標準的輸出設(shè)備,一般為屏幕,這里實現(xiàn)輸出重定向,即把輸出管道作為輸出設(shè)備。
            7)。操作D:把輸出管道的數(shù)據(jù)寫入輸出文件
            需要注意的是,管道的本質(zhì)只是一個共享的內(nèi)存區(qū)域。這個實驗中,管道區(qū)域處于父進程的地址空間中,父進程的作用是提供環(huán)境和資源,并協(xié)調(diào)子進程進行加工。
            程序源碼:
            代碼:
            #include <windows.h> 
            #include <iostream.h>
            const int BUFSIZE = 4096 ; 
            HANDLE  hChildStdinRd, hChildStdinWr, hChildStdinWrDup, 
                   hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup, 
                hSaveStdin,    hSaveStdout; 
            BOOL CreateChildProcess(LPTSTR); 
            VOID WriteToPipe(LPTSTR); 
            VOID ReadFromPipe(LPTSTR); 
            VOID ErrorExit(LPTSTR); 
            VOID ErrMsg(LPTSTR, BOOL); 
            void main( int argc, char *argv[] ) 
            {  
              // 處理輸入?yún)?shù)
              if ( argc != 4 )
                return ;
              // 分別用來保存命令行,輸入文件名(CPP/C),輸出文件名(保存編譯信息)
              LPTSTR lpProgram = new char[ strlen(argv[1]) ] ;
              strcpy ( lpProgram, argv[1] ) ;
              LPTSTR lpInputFile = new char[ strlen(argv[2]) ];
              strcpy ( lpInputFile, argv[2] ) ;
              LPTSTR lpOutputFile = new char[ strlen(argv[3]) ] ;
              strcpy ( lpOutputFile, argv[3] ) ;    
              
              SECURITY_ATTRIBUTES saAttr; 
              saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
              saAttr.bInheritHandle = TRUE; 
              saAttr.lpSecurityDescriptor = NULL; 
               
              /************************************************
               *    redirecting child process's STDOUT  *
               ************************************************/
              hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
              
              if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) 
                ErrorExit("Stdout pipe creation failed\n"); 
                
              if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) 
                ErrorExit("Redirecting STDOUT failed"); 
              
              BOOL fSuccess = DuplicateHandle(
                GetCurrentProcess(), 
                hChildStdoutRd,
                    GetCurrentProcess(), 
                &hChildStdoutRdDup ,
                0,
                    FALSE,
                    DUPLICATE_SAME_ACCESS);
                if( !fSuccess )
                    ErrorExit("DuplicateHandle failed");
                CloseHandle(hChildStdoutRd);
              
              /************************************************
               *    redirecting child process's STDIN    *
               ************************************************/
              hSaveStdin = GetStdHandle(STD_INPUT_HANDLE); 
              if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) 
                ErrorExit("Stdin pipe creation failed\n"); 
              
              if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd)) 
                ErrorExit("Redirecting Stdin failed"); 
              
              fSuccess = DuplicateHandle(
                GetCurrentProcess(), 
                hChildStdinWr, 
                GetCurrentProcess(),
                &hChildStdinWrDup, 
                0, 
                FALSE,                 
                DUPLICATE_SAME_ACCESS); 
              if (! fSuccess) 
                ErrorExit("DuplicateHandle failed"); 
              CloseHandle(hChildStdinWr);   
              /************************************************
               *      創(chuàng)建子進程(即啟動SAMPLE.EXE)    *
               ************************************************/
              fSuccess = CreateChildProcess( lpProgram );
              if ( !fSuccess ) 
                ErrorExit("Create process failed"); 
              
              // 父進程輸入輸出流的還原設(shè)置
              if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin)) 
                ErrorExit("Re-redirecting Stdin failed\n"); 
              if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) 
                ErrorExit("Re-redirecting Stdout failed\n"); 
              WriteToPipe( lpInputFile ) ;
              ReadFromPipe( lpOutputFile ); 
                      delete lpProgram ;
                      delete lpInputFile ;
                      delete lpOutputFile ;
            } 
            BOOL CreateChildProcess( LPTSTR lpProgram ) 
            { 
              PROCESS_INFORMATION piProcInfo; 
              STARTUPINFO siStartInfo;
              BOOL bFuncRetn = FALSE; 
              
              ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
              ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
              siStartInfo.cb = sizeof(STARTUPINFO); 
              
              bFuncRetn = CreateProcess ( NULL, lpProgram, NULL, NULL, TRUE, \
                            0, NULL, NULL, &siStartInfo, &piProcInfo);
              if (bFuncRetn == 0) 
              {
                ErrorExit("CreateProcess failed\n");
                return 0;
              } 
              else 
              {
                CloseHandle(piProcInfo.hProcess);
                CloseHandle(piProcInfo.hThread);
                return bFuncRetn;
              }
            }
            VOID WriteToPipe( LPTSTR lpInputFile ) 
            { 
              HANDLE hInputFile = CreateFile(lpInputFile, GENERIC_READ, 0, NULL, 
                OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); 
              if (hInputFile == INVALID_HANDLE_VALUE) 
                return ;
              BOOL fSuccess ;
              DWORD dwRead, dwWritten; 
              CHAR chBuf[BUFSIZE] = {0} ; 
              
              for (;;) 
              { 
                fSuccess = ReadFile( hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ;
                if ( !fSuccess || dwRead == 0)
                  break; 
                fSuccess = WriteFile( hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL) ;
                if ( !fSuccess ) 
                  break; 
              } 
                
              if (! CloseHandle(hChildStdinWrDup)) 
                ErrorExit("Close pipe failed\n"); 
              CloseHandle ( hInputFile ) ;
            } 
            VOID ReadFromPipe( LPTSTR lpOutputFile ) 
            { 
              HANDLE hOutputFile = CreateFile( lpOutputFile, GENERIC_READ|GENERIC_WRITE, 
                FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
              if (hOutputFile == INVALID_HANDLE_VALUE) 
                return ;
              BOOL fSuccess ;
              DWORD dwRead, dwWritten; 
              CHAR chBuf[BUFSIZE] = { 0 }; 
              
              if (!CloseHandle(hChildStdoutWr)) 
                ErrorExit("Closing handle failed"); 
              
              for (;;) 
              { 
                fSuccess = ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) ;
                if( !fSuccess || dwRead == 0) 
                {
                  break; 
                }
                fSuccess = WriteFile( hOutputFile, chBuf, dwRead, &dwWritten, NULL) ;
                if ( !fSuccess ) 
                  break; 
              } 
              CloseHandle ( hOutputFile ) ;
            } 
            VOID ErrorExit (LPTSTR lpszMessage) 
            { 
              MessageBox( 0, lpszMessage, 0, 0 ); 
            }
            
            二、命名管道
            命名管道具有以下幾個特征:
            (1)命名管道是雙向的,所以兩個進程可以通過同一管道進行交互。
            (2)命名管道不但可以面向字節(jié)流,還可以面向消息,所以讀取進程可以讀取寫進程發(fā)送的不同長度的消息。
            (3)多個獨立的管道實例可以用一個名稱來命名。例如幾個客戶端可以使用名稱相同的管道與同一個服務器進行并發(fā)通信。
            (4)命名管道可以用于網(wǎng)絡(luò)間兩個進程的通信,而其實現(xiàn)的過程與本地進程通信完全一致。
            實驗目標:在客戶端輸入數(shù)據(jù)a和b,然后發(fā)送到服務器并計算a+b,然后把計算結(jié)果發(fā)送到客戶端。可以多個客戶端與同一個服務器并行通信。
            界面設(shè)計:
             http://bbs.pediy.com/upload/2006/41/image/namedpipe.gif 
            難點所在:
            實現(xiàn)的過程比較簡單,但有一個難點。原本當服務端使用ConnectNamedPipe函數(shù)后,如果有客戶端連接,就可以直接進行交互。原來我在實現(xiàn)過程中,當管道空閑時,管道的線程函數(shù)會無限(INFINITE)阻塞。若現(xiàn)在需要停止服務,就必須結(jié)束所有的線程,TernimateThread可以作為一個結(jié)束線程的方法,但我基本不用這個函數(shù)。一旦使用這個函數(shù)之后,目標線程就會立即結(jié)束,但如果此時的目標線程正在操作互斥資源、內(nèi)核調(diào)用、或者是操作共享DLL的全局變量,可能會出現(xiàn)互斥資源無法釋放、內(nèi)核異常等現(xiàn)象。這里我用重疊I/0來解決這個問題,在創(chuàng)建PIPE時使用FILE_FLAG_OVERLAPPED標志,這樣使用ConnectNamedPipe后會立即返回,但線程的阻塞由等待函數(shù)WaitForSingleObject來實現(xiàn),等待OVERLAPPED結(jié)構(gòu)的事件對象被設(shè)置。
            客戶端主要代碼:
            代碼:
            void CMyDlg::OnSubmit() 
            {
              // 打開管道
              HANDLE hPipe = CreateFile("\\\\.\\Pipe\\NamedPipe", GENERIC_READ | GENERIC_WRITE, \
                0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ;
              if ( hPipe == INVALID_HANDLE_VALUE )
              {
                this->MessageBox ( "打開管道失敗,服務器尚未啟動,或者客戶端數(shù)量過多" ) ;
                return ;
              }
              DWORD nReadByte, nWriteByte ;
              char szBuf[1024] = {0} ;
              // 把兩個整數(shù)(a,b)格式化為字符串
              sprintf ( szBuf, "%d %d", this->nFirst, this->nSecond ) ;
              // 把數(shù)據(jù)寫入管道
              WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
              memset ( szBuf, 0, sizeof(szBuf) ) ;
              // 讀取服務器的反饋信息
              ReadFile ( hPipe, szBuf, 1024, &nReadByte, NULL ) ;
              // 把返回信息格式化為整數(shù)
              sscanf ( szBuf, "%d", &(this->nResValue) ) ;
              this->UpdateData ( false ) ;
              CloseHandle ( hPipe ) ;
            }
            
            服務端主要代碼:
            代碼:
            // 啟動服務
            void CMyDlg::OnStart() 
            {
              CString lpPipeName = "\\\\.\\Pipe\\NamedPipe" ;
              for ( UINT i = 0; i < nMaxConn; i++ )
              {
                // 創(chuàng)建管道實例
                PipeInst[i].hPipe =  CreateNamedPipe ( lpPipeName, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, \
                      PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, nMaxConn, 0, 0, 1000, NULL ) ;
                if ( PipeInst[i].hPipe == INVALID_HANDLE_VALUE )
                {
                  DWORD dwErrorCode = GetLastError () ;
                  this->MessageBox ( "創(chuàng)建管道錯誤!" ) ;
                  return ;
                }
                // 為每個管道實例創(chuàng)建一個事件對象,用于實現(xiàn)重疊IO
                PipeInst[i].hEvent  =  CreateEvent ( NULL, false, false, false ) ;
                // 為每個管道實例分配一個線程,用于響應客戶端的請求
                PipeInst[i].hTread = AfxBeginThread ( ServerThread, &PipeInst[i], THREAD_PRIORITY_NORMAL ) ;
              }
              
              this->SetWindowText ( "命名管道實例之服務器(運行)" ) ;
              this->MessageBox ( "服務啟動成功" ) ;
            }
            // 停止服務
            void CMyDlg::OnStop() 
            {
              DWORD dwNewMode = PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT ;
              for ( UINT i = 0; i < nMaxConn; i++ )
              {
                SetEvent ( PipeInst[i].hEvent ) ;
                CloseHandle ( PipeInst[i].hTread ) ;
                CloseHandle ( PipeInst[i].hPipe ) ;
              }
                
              this->SetWindowText ( "命名管道實例之服務器" ) ;
              this->MessageBox ( "停止啟動成功" ) ;
            }
            // 線程服務函數(shù)
            UINT ServerThread ( LPVOID lpParameter )
            {
              DWORD  nReadByte = 0, nWriteByte = 0, dwByte = 0 ;  
              char  szBuf[MAX_BUFFER_SIZE] = {0} ;
              PIPE_INSTRUCT  CurPipeInst = *(PIPE_INSTRUCT*)lpParameter ;
              OVERLAPPED OverLapStruct = { 0, 0, 0, 0, CurPipeInst.hEvent } ;
              while ( true )
              {
                memset ( szBuf, 0, sizeof(szBuf) ) ;  
                // 命名管道的連接函數(shù),等待客戶端的連接(只針對NT)
                ConnectNamedPipe ( CurPipeInst.hPipe, &OverLapStruct ) ;
                // 實現(xiàn)重疊I/0,等待OVERLAPPED結(jié)構(gòu)的事件對象
                WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ;
                // 檢測I/0是否已經(jīng)完成,如果未完成,意味著該事件對象是人工設(shè)置,即服務需要停止
                if ( !GetOverlappedResult ( CurPipeInst.hPipe, &OverLapStruct, &dwByte, true ) )
                  break ;
                // 從管道中讀取客戶端的請求信息
                if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, &nReadByte, NULL ) )
                {
                  MessageBox ( 0, "讀取管道錯誤!", 0, 0 ) ;
                  break ;
                }
                
                int a, b ;
                sscanf ( szBuf, "%d %d", &a, &b ) ;
                pMyDlg->nFirst    = a ;
                pMyDlg->nSecond    = b ;
                pMyDlg->nResValue  = a + b ;
                memset ( szBuf, 0, sizeof(szBuf) ) ;
                sprintf ( szBuf, "%d", pMyDlg->nResValue ) ;
                // 把反饋信息寫入管道
                WriteFile ( CurPipeInst.hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
                pMyDlg->SetDlgItemInt ( IDC_FIRST, a, true ) ;
                pMyDlg->SetDlgItemInt ( IDC_SECOND, b, true ) ;
                pMyDlg->SetDlgItemInt ( IDC_RESULT, pMyDlg->nResValue, true ) ;
                // 斷開客戶端的連接,以便等待下一客戶的到來
                DisconnectNamedPipe ( CurPipeInst.hPipe ) ;
              }
              return 0 ;
            }
            

            posted @ 2011-02-15 12:30 鄭興鋒 閱讀(791) | 評論 (0)編輯 收藏

            2010年11月4日

            setsockopt()

            目錄

            簡述:
            注釋:
            返回值:
            用法

            簡述:

              設(shè)置套接口的選項。
              #include <winsock.h>
              int PASCAL FAR setsockopt( SOCKET s, int level, int optname,
              const char FAR* optval, int optlen);
              s:標識一個套接口的描述字。
              level:選項定義的層次;目前僅支持SOL_SOCKET和IPPROTO_TCP層次。
              optname:需設(shè)置的選項。
              optval:指針,指向存放選項值的緩沖區(qū)。
              optlen:optval緩沖區(qū)的長度。

            注釋:

              setsockopt()函數(shù)用于任意類型、任意狀態(tài)套接口的設(shè)置選項值。盡管在不同協(xié)議層上存在選項,但本函數(shù)僅定義了最高的“套接口”層次上的選項。選項影響套接口的操作,諸如加急數(shù)據(jù)是否在普通數(shù)據(jù)流中接收,廣播數(shù)據(jù)是否可以從套接口發(fā)送等等。
              有兩種套接口的選項:一種是布爾型選項,允許或禁止一種特性;另一種是整形或結(jié)構(gòu)選項。允許一個布爾型選項,則將optval指向非零整形數(shù);禁止一個選項optval指向一個等于零的整形數(shù)。對于布爾型選項,optlen應等于sizeof(int);對其他選項,optval指向包含所需選項的整形數(shù)或結(jié)構(gòu),而optlen則為整形數(shù)或結(jié)構(gòu)的長度。SO_LINGER選項用于控制下述情況的行動:套接口上有排隊的待發(fā)送數(shù)據(jù),且closesocket()調(diào)用已執(zhí)行。參見closesocket()函數(shù)中關(guān)于SO_LINGER選項對closesocket()語義的影響。應用程序通過創(chuàng)建一個linger結(jié)構(gòu)來設(shè)置相應的操作特性:
              struct linger {
              int l_onoff;
              int l_linger;
              };
              為了允許SO_LINGER,應用程序應將l_onoff設(shè)為非零,將l_linger設(shè)為零或需要的超時值(以秒為單位),然后調(diào)用setsockopt()。為了允許SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff應設(shè)為零,然后調(diào)用setsockopt()。
              缺省條件下,一個套接口不能與一個已在使用中的本地地址捆綁(參見bind())。但有時會需要“重用”地址。因為每一個連接都由本地地址和遠端地址的組合唯一確定,所以只要遠端地址不同,兩個套接口與一個地址捆綁并無大礙。為了通知WINDOWS套接口實現(xiàn)不要因為一個地址已被一個套接口使用就不讓它與另一個套接口捆綁,應用程序可在bind()調(diào)用前先設(shè)置SO_REUSEADDR選項。請注意僅在bind()調(diào)用時該選項才被解釋;故此無需(但也無害)將一個不會共用地址的套接口設(shè)置該選項,或者在bind()對這個或其他套接口無影響情況下設(shè)置或清除這一選項。
              一個應用程序可以通過打開SO_KEEPALIVE選項,使得WINDOWS套接口實現(xiàn)在TCP連接情況下允許使用“保持活動”包。一個WINDOWS套接口實現(xiàn)并不是必需支持“保持活動”,但是如果支持的話,具體的語義將與實現(xiàn)有關(guān),應遵守RFC1122“Internet主機要求-通訊層”中第4.2.3.6節(jié)的規(guī)范。如果有關(guān)連接由于“保持活動”而失效,則進行中的任何對該套接口的調(diào)用都將以WSAENETRESET錯誤返回,后續(xù)的任何調(diào)用將以WSAENOTCONN錯誤返回。
              TCP_NODELAY選項禁止Nagle算法。Nagle算法通過將未確認的數(shù)據(jù)存入緩沖區(qū)直到蓄足一個包一起發(fā)送的方法,來減少主機發(fā)送的零碎小數(shù)據(jù)包的數(shù)目。但對于某些應用來說,這種算法將降低系統(tǒng)性能。所以TCP_NODELAY可用來將此算法關(guān)閉。應用程序編寫者只有在確切了解它的效果并確實需要的情況下,才設(shè)置TCP_NODELAY選項,因為設(shè)置后對網(wǎng)絡(luò)性能有明顯的負面影響。TCP_NODELAY是唯一使用IPPROTO_TCP層的選項,其他所有選項都使用SOL_SOCKET層。
              如果設(shè)置了SO_DEBUG選項,WINDOWS套接口供應商被鼓勵(但不是必需)提供輸出相應的調(diào)試信息。但產(chǎn)生調(diào)試信息的機制以及調(diào)試信息的形式已超出本規(guī)范的討論范圍。
              setsockopt()支持下列選項。其中“類型”表明optval所指數(shù)據(jù)的類型。
              選項 類型 意義
              SO_BROADCAST BOOL 允許套接口傳送廣播信息。
              SO_DEBUG BOOL 記錄調(diào)試信息。
              SO_DONTLINER BOOL 不要因為數(shù)據(jù)未發(fā)送就阻塞關(guān)閉操作。設(shè)置本選項相當于將SO_LINGER的l_onoff元素置為零。
              SO_DONTROUTE BOOL 禁止選徑;直接傳送。
              SO_KEEPALIVE BOOL 發(fā)送“保持活動”包。
              SO_LINGER struct linger FAR* 如關(guān)閉時有未發(fā)送數(shù)據(jù),則逗留。
              SO_OOBINLINE BOOL 在常規(guī)數(shù)據(jù)流中接收帶外數(shù)據(jù)。
              SO_RCVBUF int 為接收確定緩沖區(qū)大小。
              SO_REUSEADDR BOOL 允許套接口和一個已在使用中的地址捆綁(參見bind())。
              SO_SNDBUF int 指定發(fā)送緩沖區(qū)大小。
              TCP_NODELAY BOOL 禁止發(fā)送合并的Nagle算法。
              setsockopt()不支持的BSD選項有:
              選項名 類型 意義
              SO_ACCEPTCONN BOOL 套接口在監(jiān)聽。
              SO_ERROR int 獲取錯誤狀態(tài)并清除。
              SO_RCVLOWAT int 接收低級水印。
              SO_RCVTIMEO int 接收超時。
              SO_SNDLOWAT int 發(fā)送低級水印。
              SO_SNDTIMEO int 發(fā)送超時。
              SO_TYPE int 套接口類型。
              IP_OPTIONS 在IP頭中設(shè)置選項。

            返回值:

              若無錯誤發(fā)生,setsockopt()返回0。否則的話,返回SOCKET_ERROR錯誤,應用程序可通過WSAGetLastError()獲取相應錯誤代碼。
              錯誤代碼:
              WSANOTINITIALISED:在使用此API之前應首先成功地調(diào)用WSAStartup()。
              WSAENETDOWN:WINDOWS套接口實現(xiàn)檢測到網(wǎng)絡(luò)子系統(tǒng)失效。
              WSAEFAULT:optval不是進程地址空間中的一個有效部分。
              WSAEINPROGRESS:一個阻塞的WINDOWS套接口調(diào)用正在運行中。
              WSAEINVAL:level值非法,或optval中的信息非法。
              WSAENETRESET:當SO_KEEPALIVE設(shè)置后連接超時。
              WSAENOPROTOOPT:未知或不支持選項。其中,SOCK_STREAM類型的套接口不支持SO_BROADCAST選項,SOCK_DGRAM類型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE選項。
              WSAENOTCONN:當設(shè)置SO_KEEPALIVE后連接被復位。
              WSAENOTSOCK:描述字不是一個套接口。

            用法

              1.設(shè)置調(diào)用closesocket()后,仍可繼續(xù)重用該socket。調(diào)用closesocket()一般不會立即關(guān)閉socket,而經(jīng)歷TIME_WAIT的過程。
              BOOL bReuseaddr = TRUE;
              setsockopt( s, SOL_SOCKET, SO_REUSEADDR, ( const char* )&bReuseaddr, sizeof( BOOL ) );
              2. 如果要已經(jīng)處于連接狀態(tài)的soket在調(diào)用closesocket()后強制關(guān)閉,不經(jīng)歷TIME_WAIT的過程:
              BOOL bDontLinger = FALSE;
              setsockopt( s, SOL_SOCKET, SO_DONTLINGER, ( const char* )&bDontLinger, sizeof( BOOL ) );
              3.在send(),recv()過程中有時由于網(wǎng)絡(luò)狀況等原因,收發(fā)不能預期進行,可以設(shè)置收發(fā)時限:
              int nNetTimeout = 1000; //1秒
              //發(fā)送時限
              setsockopt( socket, SOL_SOCKET, SO_SNDTIMEO, ( char * )&nNetTimeout, sizeof( int ) );
              //接收時限
              setsockopt( socket, SOL_SOCKET, SO_RCVTIMEO, ( char * )&nNetTimeout, sizeof( int ) );
              4.在send()的時候,返回的是實際發(fā)送出去的字節(jié)(同步)或發(fā)送到socket緩沖區(qū)的字節(jié)(異步);系統(tǒng)默認的狀態(tài)發(fā)送和接收一次為8688字節(jié)(約
              為8.5K);在實際的過程中如果發(fā)送或是接收的數(shù)據(jù)量比較大,可以設(shè)置socket緩沖區(qū),避免send(),recv()不斷的循環(huán)收發(fā):
              // 接收緩沖區(qū)
              int nRecvBuf = 32 * 1024; //設(shè)置為32K
              setsockopt( s, SOL_SOCKET, SO_RCVBUF, ( const char* )&nRecvBuf, sizeof( int ) );
              //發(fā)送緩沖區(qū)
              int nSendBuf = 32*1024; //設(shè)置為32K
              setsockopt( s, SOL_SOCKET, SO_SNDBUF, ( const char* )&nSendBuf, sizeof( int ) );
              5.在發(fā)送數(shù)據(jù)的時,不執(zhí)行由系統(tǒng)緩沖區(qū)到socket緩沖區(qū)的拷貝,以提高程序的性能:
              int nZero = 0;
              setsockopt( socket, SOL_S0CKET, SO_SNDBUF, ( char * )&nZero, sizeof( nZero ) );
              6.在接收數(shù)據(jù)時,不執(zhí)行將socket緩沖區(qū)的內(nèi)容拷貝到系統(tǒng)緩沖區(qū):
              int nZero = 0;
              setsockopt( s, SOL_S0CKET, SO_RCVBUF, ( char * )&nZero, sizeof( int ) );
              7.一般在發(fā)送UDP數(shù)據(jù)報的時候,希望該socket發(fā)送的數(shù)據(jù)具有廣播特性:
              BOOL bBroadcast = TRUE;
              setsockopt( s, SOL_SOCKET, SO_BROADCAST, ( const char* )&bBroadcast, sizeof( BOOL ) );
              8.在client連接服務器過程中,如果處于非阻塞模式下的socket在connect()的過程中可以設(shè)置connect()延時,直到accpet()被調(diào)用(此設(shè)置只
              有在非阻塞的過程中有顯著的作用,在阻塞的函數(shù)調(diào)用中作用不大)
              BOOL bConditionalAccept = TRUE;
              setsockopt( s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, ( const char* )&bConditionalAccept, sizeof( BOOL ) );
              9.如果在發(fā)送數(shù)據(jù)的過程中send()沒有完成,還有數(shù)據(jù)沒發(fā)送,而調(diào)用了closesocket(),以前一般采取的措施是shutdown(s,SD_BOTH),但是數(shù)
              據(jù)將會丟失。
              某些具體程序要求待未發(fā)送完的數(shù)據(jù)發(fā)送出去后再關(guān)閉socket,可通過設(shè)置讓程序滿足要求:
              struct linger {
              u_short l_onoff;
              u_short l_linger;
              };
              linger m_sLinger;
              m_sLinger.l_onoff = 1; //在調(diào)用closesocket()時還有數(shù)據(jù)未發(fā)送完,允許等待
              // 若m_sLinger.l_onoff=0;則調(diào)用closesocket()后強制關(guān)閉
              m_sLinger.l_linger = 5; //設(shè)置等待時間為5秒
              setsockopt( s, SOL_SOCKET, SO_LINGER, ( const char* )&m_sLinger, sizeof( linger ) );

            posted @ 2010-11-04 14:32 鄭興鋒 閱讀(623) | 評論 (0)編輯 收藏

            控制套接口的模式。   
            #include <winsock.h>   int PASCAL FAR ioctlsocket( SOCKET s, long cmd, u_long FAR* argp);   
            s:一個標識套接口的描述字。   
            cmd:對套接口s的操作命令。   
            argp:指向cmd命令所帶參數(shù)的指針。   
            注釋:   本函數(shù)可用于任一狀態(tài)的任一套接口。它用于獲取與套接口相關(guān)的操作參數(shù),而與具體協(xié)議或通訊子系統(tǒng)無關(guān)。支持下列命令:   FIONBIO:允許或禁止套接口s的非阻塞模式。argp指向一個無符號長整型。如允許非阻塞模式則非零,如禁止非阻塞模式則為零。當創(chuàng)建一個套接口時,它就處于阻塞模式(也就是說非阻塞模式被禁止)。這與BSD套接口是一致的。WSAAsynSelect()函數(shù)將套接口自動設(shè)置為非阻塞模式。如果已對一個套接口進行了WSAAsynSelect() 操作,則任何用ioctlsocket()來把套接口重新設(shè)置成阻塞模式的試圖將以WSAEINVAL失敗。為了把套接口重新設(shè)置成阻塞模式,應用程序必須首先用WSAAsynSelect()調(diào)用(IEvent參數(shù)置為0)來禁至WSAAsynSelect()。   FIONREAD:確定套接口s自動讀入的數(shù)據(jù)量。argp指向一個無符號長整型,其中存有ioctlsocket()的返回值。如果s是SOCKET_STREAM類型,則FIONREAD返回在一次recv()中所接收的所有數(shù)據(jù)量。這通常與套接口中排隊的數(shù)據(jù)總量相同。如果S是SOCK_DGRAM 型,則FIONREAD返回套接口上排隊的第一個數(shù)據(jù)報大小。   SIOCATMARK:確實是否所有的帶外數(shù)據(jù)都已被讀入。這個命令僅適用于SOCK_STREAM類型的套接口,且該套接口已被設(shè)置為可以在線接收帶外數(shù)據(jù)(SO_OOBINLINE)。如無帶外數(shù)據(jù)等待讀入,則該操作返回TRUE真。否則的話返回FALSE假,下一個recv()或recvfrom()操作將檢索“標記”前一些或所有數(shù)據(jù)。應用程序可用SIOCATMARK操作來確定是否有數(shù)據(jù)剩下。如果在“緊急”(帶外)數(shù)據(jù)前有常規(guī)數(shù)據(jù),則按序接收這些數(shù)據(jù)(請注意,recv()和recvfrom()操作不會在一次調(diào)用中混淆常規(guī)數(shù)據(jù)與帶外數(shù)據(jù))。argp指向一個BOOL型數(shù),ioctlsocket()在其中存入返回值。   兼容性:   本函數(shù)為Berkeley套接口函數(shù)ioctl()的一個子集。其中沒有與FIOASYNC等價的命令,SIOCATMARK是套接口層次支持的唯一命令。   返回值:   成功后,ioctlsocket()返回0。否則的話,返回SOCKET_ERROR錯誤,應用程序可通過WSAGetLastError()獲取相應錯誤代碼。   錯誤代碼:   WSANOTINITIALISED:在使用此API之前應首先成功地調(diào)用WSAStartup()。   WSAENETDOWN:WINDOWS套接口實現(xiàn)檢測到網(wǎng)絡(luò)子系統(tǒng)失效。   WSAEINVAL:cmd為非法命令,或者argp所指參數(shù)不適用于該cmd命令,或者該命令   不適用于此種類型的套接口。   WSAEINPROGRESS:一個阻塞的WINDOWS套接口調(diào)用正在運行中。   WSAENOTSOCK:描述字不是一個套接口。   參見:   socket(), setsockopt(), getsockopt(), WSAAsyncSelect().   該命令     不適用于此種類型的套接口。     WSAEINPROGRESS:一個阻塞的WINDOWS套接口調(diào)用正在運行中。     WSAENOTSOCK:描述字不是一個套接口。

            posted @ 2010-11-04 12:13 鄭興鋒 閱讀(548) | 評論 (0)編輯 收藏

            2010年10月21日

             
            通信模塊圖

              
              通信關(guān)系圖
             
            其中有不足的地方, 希望朋友們提出意見.

            posted @ 2010-10-21 17:51 鄭興鋒 閱讀(282) | 評論 (0)編輯 收藏

            僅列出標題  下一頁
            久久午夜综合久久| 亚洲中文字幕无码久久精品1| 国产成年无码久久久久毛片| 色8久久人人97超碰香蕉987| 久久亚洲欧美日本精品| 久久久久国产| 亚洲综合熟女久久久30p| 久久精品国产一区二区三区日韩| 国产福利电影一区二区三区久久老子无码午夜伦不| 国产亚洲色婷婷久久99精品91 | 亚洲国产精品无码久久久蜜芽| 久久福利资源国产精品999| 久久精品中文无码资源站| 久久99精品久久久久久久久久| 成人精品一区二区久久| 久久国语露脸国产精品电影| 色综合合久久天天综合绕视看| 伊人久久久AV老熟妇色| 久久综合色之久久综合| 97久久精品人人澡人人爽| 亚洲色大成网站www久久九| 欧美大战日韩91综合一区婷婷久久青草| 99精品国产免费久久久久久下载 | 久久精品国产亚洲AV电影| 久久亚洲AV永久无码精品| 久久国产乱子精品免费女| 亚洲精品午夜国产VA久久成人| 亚洲国产精品无码久久青草 | 狠狠色伊人久久精品综合网 | 久久夜色精品国产亚洲| 久久综合亚洲欧美成人| 精品国产99久久久久久麻豆| 久久午夜福利电影| 欧美日韩精品久久久久| 国产精品gz久久久| 伊人久久综在合线亚洲2019| 久久国产精品久久| 九九99精品久久久久久| 伊人色综合久久天天| 无码任你躁久久久久久老妇App| 久久精品国产亚洲AV不卡|