青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

2013年6月1日


本文主要討論如何將一個win32項目,移植到android上面,網上很多文章說的不一樣,因為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然后輸入支持系統版本,android是向下兼容的。我這里寫5,也就是android2.1-update1
本地圖片,請重新上傳
然后項目就建立好了,你會發現在cocos2d-x安裝目錄下,多了個Test文件夾

第二步:導入代碼和資源

#1打開Test

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

第三步:編輯mk

#1注意有坑!android項目里有一大堆mk,而且不同的教程說的mk位置還不一樣!你編輯錯誤了,是無效的(跟你實際使用的mk也有關系)。在這個例子中,我們使用的是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導入成功,然后就是編譯執行了。cocos2d-x作者推薦用sh編譯so,再用eclipse編譯成apk。
本地圖片,請重新上傳

其他注意事項:


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

 

 

 

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


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

環境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的安裝就不多說了,網上多的是。

將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文件

在文件最后添加如下內容

 

  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
上面是我的系統中使用的路徑,同學們需要根據自己的實際情況進行修改。

 

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

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

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

 

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

 

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

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

2011年11月21日

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

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

2011年11月20日

  最近在用VC++開發一個小工具,平時用慣了.NET,用起VC++最郁悶的就是字符串處理。當然最最讓人難于琢磨的就是字符集,編碼之間的轉換。通過這幾天的研究,終于明白了Unicode和UTF-8之間編碼的區別。Unicode是一個字符集,而UTF-8是Unicode的其中一種,Unicode是定長的都為雙字節,而UTF-8是可變的,對于漢字來說Unicode占有的字節比UTF-8占用的字節少1個字節。Unicode為雙字節,而UTF-8中漢字占三個字節。
                        網魂小兵 http://xdotnet.cnblogs.com
    UTF-8編碼字符理論上可以最多到6個字節長,然而16位BMP(Basic Multilingual Plane)字符最多只用到3字節長。下面看一下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 的位置由字符編碼數的二進制表示的位填入, 越靠右的 x 具有越少的特殊意義,只用最短的那個足夠表達一個字符編碼數的多字節串。 注意在多字節串中, 第一個字節的開頭"1"的數目就是整個串中字節的數目。而第一行中以0開頭,是為了兼容ASCII編碼,為一個字節,第二行就為雙字節字符串,第三行為3字節,如漢字就屬于這種,以此類推。(個人認為:其實我們可以簡單的把前面的1的個數看成字節數)
                         網魂小兵 http://xdotnet.cnblogs.com
    為了要將Unicode轉換為UTF-8,當然要知道他們的區別到底在什么地方。下面來看一下,在Unicode中的編碼是怎樣轉換成UTF-8的,在UTF-8中,如果一個字符的字節小于0x80(128)則為ASCII字符,占一個字節,可以不用轉換,因為UTF-8兼容ASCII編碼。假如在Unicode中漢字“你”的編碼為“u4F60”,把它轉換為二進制為100111101100000,然后按照UTF-8的方法進行轉換。可以將Unicode二進制從地位往高位取出二進制數字,每次取6位,如上述的二進制就可以分別取出為如下所示的格式,前面按格式填補,不足8位用0填補。

      
           unicode:  100111101100000                  4F60

           utf-8:    11100100,10111101,10100000       E4BDA0


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

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

    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!
                            網魂小兵 http://xdotnet.cnblogs.com
    當然在編程過程中不可能只轉換一個字符,這里需要注意的是字符的長度一定要算清楚,不然會帶來...以上就是我這幾天研究的結果,至于Unicode的轉換為GB2312在MFC中Windows有自帶的API(WideCharToMultiByte)可以轉換。這樣也就能夠將UTF-8格式轉換為GB2312了,這里就不再贅述,如果大家有更好的方法希望指教。

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

2011年11月16日

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

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

2011年3月22日

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

   
  














 

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

2011年2月15日

Windows系統編程之進程間通信
北極星2003 當前離線


Windows 的IPC(進程間通信)機制主要是異步管道和命名管道。(至于其他的IPC方式,例如內存映射、郵槽等這里就不介紹了)
管道(pipe)是用于進程間通信的共享內存區域。創建管道的進程稱為管道服務器,而連接到這個管道的進程稱為管道客戶端。一個進程向管道寫入信息,而另外一個進程從管道讀取信息。
異步管道是基于字符和半雙工的(即單向),一般用于程序輸入輸出的重定向;命名管道則強大地多,它們是面向消息和全雙工的,同時還允許網絡通信,用于創建客戶端/服務器系統。
一、異步管道(實現比較簡單,直接通過實例來講解)
實驗目標:當前有sample.cpp, sample.exe, sample.in這三個文件,sample.exe為sample.cpp的執行程序,sample.cpp只是一個簡單的程序示例(簡單求和),如下:
代碼:
#include <iostream.h>
int main()
{
  int a, b ;
  while ( cin >> a >> b && ( a || b ) )
    cout << a + b << endl ;
  return 0;
}
Sample.in文件是輸入文件,內容:
32 433
542 657
0 0
要求根據sample.exe和它的輸入數據,把輸出數據重定向到sample.out
流程分析:實際這個實驗中包含兩個部分,把輸入數據重定向到sample.exe 和把輸出數據重定向到sample.out。在命令行下可以很簡單的實現這個功能“sample <sample.in >sample.out”,這個命令也是利用管道特性實現的,現在我們就根據異步管道的實現原理自己來實現這個功能。
管道是基于半雙工(單向)的,這里有兩個重定向的過程,顯然需要創建兩個管道,下面給出流程圖:
 
異步管道實現的流程圖說明:
1)。父進程是我們需要實現的,其中需要創建管道A,管道B,和子進程,整個實現流程分為4個操作。
2)。管道A:輸入管道
3)。管道B:輸出管道
4)。操作A:把輸入文件sample.in的數據寫入輸入管道(管道A)
5)。操作B:子進程從輸入管道中讀取數據,作為該進程的加工原料。通常,程序的輸入數據由標準的輸入設備輸入,這里實現輸入重定向,即把輸入管道作為輸入設備。
6)。操作C:子進程把加工后的成品(輸出數據)輸出到輸出管道。通常,程序的輸出數據會輸出到標準的輸出設備,一般為屏幕,這里實現輸出重定向,即把輸出管道作為輸出設備。
7)。操作D:把輸出管道的數據寫入輸出文件
需要注意的是,管道的本質只是一個共享的內存區域。這個實驗中,管道區域處于父進程的地址空間中,父進程的作用是提供環境和資源,并協調子進程進行加工。
程序源碼:
代碼:
#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[] ) 
{  
  // 處理輸入參數
  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);   
  /************************************************
   *      創建子進程(即啟動SAMPLE.EXE)    *
   ************************************************/
  fSuccess = CreateChildProcess( lpProgram );
  if ( !fSuccess ) 
    ErrorExit("Create process failed"); 
  
  // 父進程輸入輸出流的還原設置
  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)命名管道不但可以面向字節流,還可以面向消息,所以讀取進程可以讀取寫進程發送的不同長度的消息。
(3)多個獨立的管道實例可以用一個名稱來命名。例如幾個客戶端可以使用名稱相同的管道與同一個服務器進行并發通信。
(4)命名管道可以用于網絡間兩個進程的通信,而其實現的過程與本地進程通信完全一致。
實驗目標:在客戶端輸入數據a和b,然后發送到服務器并計算a+b,然后把計算結果發送到客戶端。可以多個客戶端與同一個服務器并行通信。
界面設計:
 http://bbs.pediy.com/upload/2006/41/image/namedpipe.gif 
難點所在:
實現的過程比較簡單,但有一個難點。原本當服務端使用ConnectNamedPipe函數后,如果有客戶端連接,就可以直接進行交互。原來我在實現過程中,當管道空閑時,管道的線程函數會無限(INFINITE)阻塞。若現在需要停止服務,就必須結束所有的線程,TernimateThread可以作為一個結束線程的方法,但我基本不用這個函數。一旦使用這個函數之后,目標線程就會立即結束,但如果此時的目標線程正在操作互斥資源、內核調用、或者是操作共享DLL的全局變量,可能會出現互斥資源無法釋放、內核異常等現象。這里我用重疊I/0來解決這個問題,在創建PIPE時使用FILE_FLAG_OVERLAPPED標志,這樣使用ConnectNamedPipe后會立即返回,但線程的阻塞由等待函數WaitForSingleObject來實現,等待OVERLAPPED結構的事件對象被設置。
客戶端主要代碼:
代碼:
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 ( "打開管道失敗,服務器尚未啟動,或者客戶端數量過多" ) ;
    return ;
  }
  DWORD nReadByte, nWriteByte ;
  char szBuf[1024] = {0} ;
  // 把兩個整數(a,b)格式化為字符串
  sprintf ( szBuf, "%d %d", this->nFirst, this->nSecond ) ;
  // 把數據寫入管道
  WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;
  memset ( szBuf, 0, sizeof(szBuf) ) ;
  // 讀取服務器的反饋信息
  ReadFile ( hPipe, szBuf, 1024, &nReadByte, NULL ) ;
  // 把返回信息格式化為整數
  sscanf ( szBuf, "%d", &(this->nResValue) ) ;
  this->UpdateData ( false ) ;
  CloseHandle ( hPipe ) ;
}
服務端主要代碼:
代碼:
// 啟動服務
void CMyDlg::OnStart() 
{
  CString lpPipeName = "\\\\.\\Pipe\\NamedPipe" ;
  for ( UINT i = 0; i < nMaxConn; i++ )
  {
    // 創建管道實例
    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 ( "創建管道錯誤!" ) ;
      return ;
    }
    // 為每個管道實例創建一個事件對象,用于實現重疊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 ( "停止啟動成功" ) ;
}
// 線程服務函數
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) ) ;  
    // 命名管道的連接函數,等待客戶端的連接(只針對NT)
    ConnectNamedPipe ( CurPipeInst.hPipe, &OverLapStruct ) ;
    // 實現重疊I/0,等待OVERLAPPED結構的事件對象
    WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ;
    // 檢測I/0是否已經完成,如果未完成,意味著該事件對象是人工設置,即服務需要停止
    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 鄭興鋒 閱讀(808) | 評論 (0)編輯 收藏

2010年11月4日

setsockopt()

目錄

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

簡述:

  設置套接口的選項。
  #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:需設置的選項。
  optval:指針,指向存放選項值的緩沖區。
  optlen:optval緩沖區的長度。

注釋:

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

返回值:

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

用法

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

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

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

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

2010年10月21日

 
通信模塊圖

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

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

僅列出標題  下一頁
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产精品亚洲视频| 亚洲大片av| 国产精品永久| 欧美电影电视剧在线观看| 久久婷婷麻豆| 美女网站久久| 欧美国产日韩亚洲一区| 欧美日韩亚洲在线| 国产精品视频自拍| 韩日欧美一区二区| 在线欧美日韩| 一区二区三区欧美在线| 香蕉久久久久久久av网站| 久久精品亚洲乱码伦伦中文| 免费久久99精品国产| 亚洲精品在线视频| 亚洲欧美久久久| 久久综合久色欧美综合狠狠| 欧美国产日韩一区二区| 国产精品女同互慰在线看| 黑人操亚洲美女惩罚| 亚洲精品之草原avav久久| 亚洲欧美日韩天堂| 免费永久网站黄欧美| aa级大片欧美三级| 久久久一本精品99久久精品66| 欧美日韩岛国| 国内视频精品| 一区二区三区日韩欧美| 久久综合伊人77777| 亚洲精品影视| 麻豆精品视频在线| 国产美女在线精品免费观看| 亚洲国产日韩在线一区模特| 亚洲欧美日韩国产综合在线 | 欧美成熟视频| 国产精品一区二区久激情瑜伽| 亚洲国产精品久久人人爱蜜臀| 亚洲免费中文字幕| 亚洲高清毛片| 久久精品视频网| 国产精品亚洲а∨天堂免在线| 亚洲国产精品一区二区第一页| 欧美一区二区高清在线观看| 亚洲区欧美区| 老牛国产精品一区的观看方式| 国产老女人精品毛片久久| 制服丝袜亚洲播放| 亚洲第一精品电影| 午夜伦欧美伦电影理论片| 欧美国产日韩在线观看| 欧美一区久久| 国产女人18毛片水18精品| 亚洲一级黄色av| 日韩视频中文字幕| 欧美精品一区二区三区很污很色的 | 久久久999国产| 国产精品一区二区久久精品| 国产精品99久久久久久久久久久久| 欧美岛国在线观看| 久久人人超碰| 亚洲高清免费在线| 美女尤物久久精品| 久久视频一区| 亚洲高清不卡在线观看| 欧美激情在线狂野欧美精品| 猛干欧美女孩| 日韩视频一区二区三区| 亚洲欧洲精品一区二区三区不卡| 欧美wwwwww| 国产精品99久久久久久久vr| 在线一区二区三区四区五区| 国产精品日韩精品| 欧美一区影院| 久久精品五月| 亚洲人成人一区二区三区| 亚洲激情电影在线| 欧美午夜三级| 久久精品网址| 欧美不卡激情三级在线观看| 在线亚洲国产精品网站| 一区二区不卡在线视频 午夜欧美不卡在| 欧美日本免费| 欧美一区二区日韩一区二区| 亚洲男同1069视频| 激情综合自拍| 亚洲国产精品久久久久婷婷884| 欧美啪啪一区| 欧美在线日韩在线| 另类尿喷潮videofree| 一本色道精品久久一区二区三区| 国产精品99久久久久久白浆小说 | 久久综合综合久久综合| 噜噜噜在线观看免费视频日韩| 日韩亚洲精品电影| 午夜精品一区二区三区在线| 在线看国产一区| 亚洲精品中文字幕女同| 国产视频精品网| 亚洲人成免费| 好看的日韩av电影| 亚洲一区二区免费在线| 亚洲国产成人久久综合一区| 亚洲一区二区高清| 久久美女艺术照精彩视频福利播放| 在线观看国产一区二区| 亚洲视频1区| 亚洲日本无吗高清不卡| 亚洲综合色激情五月| 亚洲精品在线免费| 久久青草欧美一区二区三区| 亚洲欧美日韩国产中文| 欧美女激情福利| 欧美jizz19性欧美| 国产精品亚洲综合久久| 亚洲精品美女| 亚洲国产精彩中文乱码av在线播放| 一区二区三区www| 亚洲福利国产精品| 久久九九热re6这里有精品| 亚洲视频综合在线| 欧美精品123区| 久久综合五月天婷婷伊人| 国产日韩精品在线播放| 亚洲午夜一区二区三区| 日韩视频免费在线| 欧美成人一区二免费视频软件| 老司机久久99久久精品播放免费| 国产女精品视频网站免费| 亚洲一级免费视频| 亚洲欧美日韩一区在线| 欧美日韩视频在线第一区| 亚洲日韩视频| 国产精品99久久久久久www| 欧美理论在线播放| 亚洲精品日韩久久| 日韩五码在线| 欧美国产国产综合| 欧美黄色一级视频| 亚洲片在线观看| 欧美成人国产| 91久久在线观看| 一区二区三区你懂的| 欧美日韩日日骚| 99re热这里只有精品视频| 一道本一区二区| 欧美午夜视频在线观看| 一区二区三区日韩| 香蕉久久夜色精品国产使用方法| 国产精品高潮呻吟| 亚洲欧美精品在线观看| 久久综合久久美利坚合众国| 在线成人www免费观看视频| 久久婷婷综合激情| 亚洲福利在线看| 亚洲伊人伊色伊影伊综合网| 国产女主播在线一区二区| 欧美在线亚洲在线| 欧美国产高潮xxxx1819| 9i看片成人免费高清| 国产精品久久久久久模特| 欧美一区二区视频网站| 麻豆精品国产91久久久久久| 亚洲国产欧美一区二区三区同亚洲 | 久久乐国产精品| 欧美黄色免费网站| 亚洲一区中文| 亚洲无限av看| 国产日韩成人精品| 久久久综合精品| 亚洲开发第一视频在线播放| 亚洲午夜性刺激影院| 国产欧美一区二区在线观看| 久久精品国产亚洲aⅴ| 亚洲国产视频a| 欧美亚洲一区二区在线观看| 激情久久一区| 欧美日韩精品一区二区三区四区 | 美女国产精品| 亚洲性av在线| 在线观看日韩精品| 国产精品国产福利国产秒拍| 久久久亚洲人| 午夜欧美不卡精品aaaaa| 欧美激情视频在线播放| 午夜久久久久久久久久一区二区| 在线精品视频一区二区三四| 国产精品亚洲综合天堂夜夜| 欧美黑人国产人伦爽爽爽| 亚洲一区二区黄| 亚洲精品小视频在线观看| 久久综合久久88| 欧美一级视频| 亚洲一品av免费观看| 亚洲经典一区| 在线免费不卡视频| 国内精品伊人久久久久av影院| 国产精品久久国产三级国电话系列| 美国三级日本三级久久99| 久久岛国电影|