幾種網絡編程方式:html
ISAPI、CGI、WinInet、Winsock編程
它們之間的區別:windows
1) ISAPI主要是開發基於瀏覽器客戶端與服務器端程序。效率比CGI方式高,並且也擴展了CGI沒有的一些功能。(基於TCP/IP模型中的應用層)api
2) CGI主要是開發基於瀏覽器客戶端與服務器端程序。(基於TCP/IP模型中的應用層)瀏覽器
3) WinInet主要是開發客戶端程序。(基於TCP/IP模型中的應用層)服務器
4) Winsock主要是基於socket來開發客戶端與服務器端程序。(基於TCP/IP模型中的各層)要想開發低層協議的程序的話就要了解協議的報文格式。網絡
網絡基礎知識:數據結構
網絡硬件多線程
數據通信原理 (詳見http://download.csdn.net/source/1196517)併發
OSI七層網絡模型與TCP/IP四層網絡模型 (詳見http://bbs.51cto.com/topic/thread-396621.html)
網絡原理和協議 (詳見http://www.cnpaf.net/)
Winsock
網絡編程:
建議,把機械工業出版社出的《Windows網絡編程技術》看N遍後,再利用MFC或者SDK編寫一些小的通訊例程,而後編寫較大規模的網絡程序,最後你就明白了網絡編程了!
《Windows網絡編程技術》專門討論Windows網絡編程技術,覆蓋Windows 95/98/NT 4/2000/CE平臺。內容包括NetBIOS和Windows重定向器方法、Winsock方法、客戶端遠程訪問服務器方法。本書論述深刻淺出、用大量實例詳解了微軟網絡API函數的應用。
《TCP/IP詳解,卷1:協議》是一本完整而詳細的TCP/IP協議指南。描述了屬於每一層的各個協議以及它們如何在不一樣操做系統中運行。
《網絡通訊編程實用案例精選》是一本介紹利用vlsuaIC++進行網絡通訊程序開發的書籍。書中精選了大量網絡實例,涵蓋了本地汁算機網絡編程、局域網網絡通訊編程、IE編程、網絡通訊協議編程、串口通訊編程、代理服務器編程和高級網絡通訊編程.
RFC文檔目錄:http://oss.org.cn/man/develop/rfc/default.htm
ACE:ACE自適配通訊環境(ADAPTIVE Communication Environment)是能夠自由使用、開放源碼的面向對象框架,在其中實現了許多用於併發通訊軟件的核心模式。ACE提供了一組豐富的可複用C++ Wrapper Facade(包裝外觀)和框架組件,可跨越多種平臺完成常見的通訊軟件任務,其中包括:事件多路分離和事件處理器分派、信號處理、服務初始化、進程間通訊、共享內存管理、消息路由、分佈式服務動態(重)配置、併發執行和同步,等等。ACE資料參考:http://docs.huihoo.com/ace_tao/index.html
建議在www.codeproject.com、http://www.codeguru.com/網站上找些老外寫的網絡代碼研究研究,最好能參加實際的網絡項目,這樣能見識更多成熟的網絡類庫。最好能參加實際的網絡項目,這樣能見識更多成熟的網絡類庫。
開源網絡封裝庫 : ACE,ICE,asio,cppsocket,netclass,poco,SimpleSocket,socketman,Sockets 開源下載工具 fdm, eMulePlus,eMule 開源FTP FileZilla 開源服務器 Apache 網遊服務器開源框架 GNE,HawkNL,RakNet,SDL_net
網絡協議分析軟件:
Sniffer工具
Wireshark 開源的經典的協議分析工具Wireshark, http://www.wireshark.org/
WPE -------抓包
Ethereal -------協議分析 SockMon5 -------抓包及錯誤分析
Windows網絡編程細節問題: 1. 若是在已經處於 ESTABLISHED狀態下的socket(通常由端口號和標誌符區分)調用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;則功能和B)做用相同;
m_sLinger.l_linger=5;//(允許逗留的時間爲5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof
(linger));
注意點:
A.在設置了逗留延時,用於一個非阻塞的socket是做用不大的,最好不用;
B.若是想要程序不經歷SO_LINGER須要設置SO_DONTLINGER,或者設置l_onoff=0;
10.一個用的比較少的是在SDI或者是Dialog的程序中,能夠記錄socket的調試信息:
BOOL bDebug=TRUE;
setsockopt(s,SOL_SOCKET,SO_DEBUG,(const char*)&bDebug,sizeof(BOOL));
11.每每經過setsockopt()設置了緩衝區大小,但還不能知足數據的傳輸需求,通常習慣是本身寫個處理網絡緩衝的類,動態分配內存。
十二、#include <Afxsock.h>,#include<winsock2.h>衝突問題
解決方法:在StdAfx.h 頭文件中添加winsock2.h,Afxsock.h
先#include <winsock2.h> 再#include <Afxsock.h>
1三、獲取數據包,通常來講想獲取數據包可使用IP_HDRINCL選項,可是在Windows 2000/XP中setsockopt()中IP_HDRINCL是個不合法的選項,可是可使用 WSAIoctl() 函數調用SIO_RCVALL捕獲IP數據包。簡單步驟以下: 1)、Create a raw socket. 2)、Bind the socket to the local IP over which the traffic is to be sniffed. 3)、WSAIoctl() the socket with SIO_RCVALL to give it sniffing powers. 4)、Put the socket in an infinite loop of recvfrom. 5)、n' joy! the Buffer from recvfrom.
1四、IP、TCP、UDP、ICMP數據包格式 /*The IP header */ typedef struct tagIPHEADER{ unsigned char version:4; unsigned char header_len:4; unsigned char tos; unsigned short total_len; unsigned short ident; unsigned short flags; unsigned char ttl; unsigned char proto; unsigned short checksum; unsigned int sourceIP; unsigned int destIP; }IPHEADER;
struct TCPPacketHead{ WORD SourPort; WORD DestPort; DWORD SeqNo; DWORD AckNo; BYTE HLen; BYTE Flag; WORD WndSize; WORD ChkSum; WORD UrgPtr; };
struct ICMPPacketHead { BYTE Type; BYTE Code; WORD ChkSum; };
struct UDPPacketHead { WORD SourPort; WORD DestPort; WORD Len; WORD ChkSum; };
1五、幾種winsock I/O模型比較: select模型核心就是select函數,它可用於判斷套接字上是否存在數據,或者可否向一個套接字寫入數據。這個函數能夠有效地防止應用程序在套接字處於阻塞模式中時,send或recv進入阻塞狀態;同時也能夠防止產生大量的WSAEWOULDBLOCK錯誤select的優點是可以從單個線程的多個套接字上進行多重鏈接及I/O。
WSAAsyncSelect 模型是以消息機制爲基礎,可以處理必定的客戶鏈接量,可是擴展性也不是很好。由於消息泵很快就會阻塞,下降了消息處理的速度。WSAAsyncSelect和WSAEventSelect模型提供了讀寫數據能力的異步通知,但他們不提供異步數據傳送,而重疊及完成端口提供異步數據的傳送。
WSAEventSelect 模型以時間爲基礎的網絡事件通知,可是與WSAAsyncSelect不一樣的是,它主要是由事件對象句柄完成的,而不是經過窗口。可是一個線程只能等待64個事件(須要開闢多個線程解決),伸縮性不如完成端口。
重疊模型可使程序能達到更佳的系統性能。基本設計原理就是讓應用程序使用重疊的數據結構,一次投遞一個或多個I/O請求。針對這些提交的請求,在他們完成以後,應用程序可爲他們提供服務。它又分爲兩種實現方法:事件通知和完成例程。重疊I/O模型事件通知依賴於等待事件通知的線程數(WSAWaitForMultipleEvents調用的每一個線程,該I/O模型一次最多都只能支持6 4個套接字。),處理客戶通訊時,大量線程上下文的切換是它們共同的制約因素。
完成端口提供了最好的伸縮性,每每可使系統達到最好的性能,是處理成千上萬的套接字的首選。從本質上說,完成端口模型要求建立一個windows完成端口對象,該對象經過指定數量的線程,對重疊I/O請求進行管理,以便爲已經完成的重疊I/O請求提供服務。可是完成端口只是支持NT系統、WIN2000系統。
重疊模型和完成端口模型的應用程序通知緩衝區收發系統直接使用數據,也就是說,若是應用程序投遞了一個10KB大小的緩衝區來接收數據,且數據已經到達套接字,則該數據將直接被拷貝到投遞的緩衝區。 而select模型、WSAAsyncSelect 模型、WSAEventSelect 模型,數據到達並拷貝到單套接字接收緩衝區中,此時應用程序會被告知能夠讀入的容量。當應用程序調用接收函數以後,數據才從單套接字緩衝區拷貝到應用程序的緩衝區,差異就體現出來了。
1六、服務器與客戶端IO模型選擇
對於如何挑選最適合本身應用程序的I/O模型已經很明晰了。同開發一個簡單的運行多線程的鎖定模式應用相比,其餘每種I/O模型都須要更爲複雜的編程工做。所以,針對客戶機和服務器應用開發模型的選擇,有如下原則。
1). 客戶端
若打算開發一個客戶機應用,令其同時管理一個或多個套接字,那麼建議採用重疊I/O或WSAEventSelect模型
,以便在必定程度上提高性能。然而,假如開發的是一個以Windows爲基礎的應用程序,要進行窗口消息的管理,那麼WSAAsyncSelect模型恐怕是一種最好的選擇,由於WSAAsyncSelect自己即是從Windows消息模型借鑑來的。採用這種模型,程序需具有消息處理功能。
2). 服務器端
若開發的是一個服務器應用,要在一個給定的時間,同時控制多個套接字,建議採用重疊I/O模型,這一樣是從性能角度考慮的。可是,若是服務器在任何給定的時間,都會爲大量I/O請求提供服務,便應考慮使用I/O完成端口模型,從而得到更佳的性能。
1七、shutdown、closesocket區別
shutdown 從容關閉,爲了保證通訊雙方都可以收到應用程序發出的全部數據,一個合格的應用程序的作法是通知接受雙發都不在發送數據!這就是所謂的「正常關閉」套接字的方法,而這個方法就是由shutdown函數,傳遞給它的參數有SD_RECEIVE,SD_SEND,SD_BOTH三種,若是是SD_RECEIVE就表示不容許再對此套接字調用接受函數。這對於協議層沒有影響,另外對於tcp套接字來講,不管數據是在等候接受仍是即將抵達,都要重置鏈接(注意對於udp協議來講,仍然接受並排列傳入的數據,所以udp套接字而言shutdown毫無心義)。若是選擇SE_SEND,則表示不容許再調用發送函數。對於tcp套接字來講,這意味着會在全部數據發送出並獲得接受端確認後產生一個FIN包。若是指定SD_BOTH,答案不言而喻。 closesocket 正式關閉,關閉鏈接,釋放全部相關的資源。由於無鏈接協議沒有鏈接,因此不會有正式關閉和從容關閉,直接調用closesocket函數。
1八、TCP連接三次握手、終止連接四次握手
1九、getpeername 、getsockname
getpeername 函數用於得到通訊方的套接字地址信息,該信息上關於已創建鏈接的那個套接字的。 getsockname 函數是getpeername的對應函數。它返回的是指定套接字的本地接口的地址信息。
20、MFC下CSocket編程注意事項
1)、在使用MFC編寫socket程序時,必需要包含<afxsock.h>都文件。 2)、AfxSocketInit() 這個函數,在使用CSocket前必定要先調用該函數,不然使用CSocket會出錯。 3)、CSocket::Create 的接口就是, 實現上還執行了 CSocket::Bind , 很是不容易被發現。若是是以 Create 方法初始化的前提下不能再調用 Bind ,要不必定出錯。通常寫服務器程序都不要用Create 爲好,用下面的
CSocket::Socket 初始化而後Bind。
2一、winsock 有兩個不一樣的版本
winsock 有兩個不一樣的版本,初版很old了,win95時代的,win2000後推崇第二版winsock 2, 出了主板本號外,還有子版本號,這些功能上有差異,winsock2 支持原始套接字編程, MFC 還封裝了winsock,使用WINSOCK.h 要用到WSOCK32.LIB, 還有一些擴展api功能,須要MSWSOCK.h MSWSOCK.DLL 。 如今winsock.h winsock2.h 都用ws2_32.lib。
2二、sockaddr_in , sockaddr , in_addr區別 struct sockaddr { unsigned short sa_family; char sa_data[14]; }; 上面是通用的socket地址,具體到Internet socket,用下面的結構,兩者能夠進行類型轉換 struct sockaddr_in { short int sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; }; struct in_addr就是32位IP地址。 struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un;
#define s_addr S_un.S_addr }; inet_addr()是將一個點分制的IP地址(如192.168.0.1)轉換爲上述結構中須要的32位IP地址(0xC0A80001)。
填值的時候使用sockaddr_in結構,而做爲函數(如socket, listen, bind等)的參數傳入的時候轉換成sockaddr結構就好了,畢竟都是16個字符長。
一般的用法是: int sockfd; struct sockaddr_in my_addr; sockfd = socket(AF_INET, SOCK_STREAM, 0); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(MYPORT); my_addr.sin_addr.s_addr = inet_addr("192.168.0.1"); bzero(&(my_addr.sin_zero), 8); bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); 想來你是要進行網絡編程,使用socket, listen, bind等函數。你只要記住,填值的時候使用sockaddr_in結構,而做爲函數的參數傳入的時候轉換成sockaddr結構就好了,畢竟都是16個字符長。 2三、幾個特殊的地址
INADDR_LOOPBACK (127.0.0.1) 老是表明經由迴環設備的本地主機; INADDR_ANY
(0.0.0.0) 表示任何可綁定的地址; INADDR_BROADCAST (255.255.255.255) 表示任何主機。 INADDR_ANY 的具體含義是,綁定到0.0.0.0。此時,對全部的地址都將是有效的,若是系統考慮冗餘,採用多個網卡的話,那麼使用此種bind,將在全部網卡上進行綁定。在這種狀況下,你能夠收到發送到全部有效地址上數據包。 例如: SOCKADDR_IN Local; Local.sin_addr.s_addr = htonl(INADDR_ANY); 另一種方式以下: SOCKADDR_IN Local; hostent* thisHost = gethostbyname(""); char* ip = inet_ntoa(*(struct in_addr *)*thisHost->h_addr_list); Local.sin_addr.s_addr = inet_addr(ip); 在這種方式下,將在系統中當前第一個可用地址上進行綁定。在多網卡的環境下,可能會出問題。
2四、常見協議
FTP協議:http://blog.csdn.net/superman419/archive/2009/04/10/4063476.aspx SMTP協議:http://www.cnpaf.net/class/smtp/ POP3協議:http://www.cnpaf.net/class/pop3/ http://www.yesky.com/20020305/1600243.shtml ICMP協議:http://blog.csdn.net/byxdaz/archive/2007/08/01/1720971.aspx RAS協議:http://blog.ixpub.net/html/94/10181094-31509.html TAPI協議:http://blog.csdn.net/chszs/archive/2008/12/08/3475908.aspx Telnet協議:http://www.cnblogs.com/liuweijian/archive/2005/09/12/235493.html HTTP協議:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html 代理協議socks:http://blog.csdn.net/byxdaz/archive/2010/03/31/5439291.aspx
2五、爲何須要htons(), ntohl(), ntohs(),htons() 函數? 在C/C++寫網絡程序的時候,每每會遇到字節的網絡順序和主機順序的問題。這是就可能用到htons(), ntohl(), ntohs(),htons()這4個函數。
網絡字節順序與本地字節順序之間的轉換函數:
htonl()--"Host to Network Long" ntohl()--"Network to Host Long" htons()--"Host to Network Short" ntohs()--"Network to Host Short"
之因此須要這些函數是由於計算機數據表示存在兩種字節順序:NBO與HBO
網絡字節順序NBO(Network Byte Order): 按從高到低的順序存儲,在網絡上使用統一的網絡字節順序,能夠避免兼容性問題。
主機字節順序(HBO,Host Byte Order): 不一樣的機器HBO不相同,與CPU設計有關,數據的順序是由cpu決定的,而與操做系統無關。 如 Intel x86結構下,short型數0x1234表示爲34 12, int型數0x12345678表示爲78 56 34 12 如IBM power PC結構下,short型數0x1234表示爲12 34, int型數0x12345678表示爲12 34 56 78 因爲這個緣由不一樣體系結構的機器之間沒法通訊,因此要轉換成一種約定的數序,也就是網絡字節順序,其實就是如同power pc那樣的順序 。在PC開發中有ntohl和htonl函數能夠用來進行網絡字節和主機字節的轉換。
2六、如何查詢端口被佔用的程序
你們在啓動服務器時,有時正常啓動有時又啓動不了是怎麼回事呢??那爲何關掉迅雷等軟件就又好了呢??如今就來給你們講解一下,
這些端口若是被其餘程序佔用就不能正常啓動,好比有時啓動時會提示WEB啓動失敗,其實就是80端口被佔用了,而迅雷等下載軟件偏偏就是佔用了80端口,關掉就好了。但有時迅雷等都沒有開也啓動不了,那就是別的東西佔用了,那怎麼辦呢?我來叫你查看端口並關掉的方法。 1.在開始--運行 裏面輸入cmd點回車,會出現運行窗口。 2.在提示符後輸入netstat -ano回車,找到tcp 80端口對應的pid,好比1484. 3.ctrl+alt+del打開任務管理器,選進程,這裏有不少正在運行的程序怎麼找?別急點上面的 查看--選擇列--在PID(進程標示符)前面打鉤。好了,下面的進程前面都有了PID號碼。這時上一步找到的PID就有用了,找到1484,好比PEER.EXE什麼的,結束進程吧。這時再開服務器,看WEB能夠啓動了!