幾種網絡編程方式:html
ISAPI、CGI、WinInet、Winsock編程
它們之間的差異:windows
1) ISAPI主要是開發基於瀏覽器client與server端程序。效率比CGI方式高,而且也擴展了CGI沒有的一些功能。(基於TCP/IP模型中的應用層)api
2) CGI主要是開發基於瀏覽器client與server端程序。(基於TCP/IP模型中的應用層)瀏覽器
3) WinInet主要是開發client程序。(基於TCP/IP模型中的應用層)網絡
4) Winsock主要是基於socket來開發client與server端程序。(基於TCP/IP模型中的各層)要想開發低層協議的程序的話就要了解協議的報文格式。數據結構
網絡基礎知識:多線程
網絡硬件 併發
數據通信原理 (詳見http://download.csdn.net/source/1196517)app
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方法、client遠程訪問server方法。本書論述深刻淺出、用大量實例具體解釋了微軟網絡API函數的應用。
《TCP/IP具體解釋,卷1:協議》是一本完整而具體的TCP/IP協議指南。描寫敘述了屬於每一層的各個協議以及它們怎樣在不一樣操做系統中執行。
《網絡通訊編程有用案例精選》是一本介紹利用vlsuaIC++進行網絡通訊程序開發的書籍。書中精選了大量網絡實例,涵蓋了本地汁算機網絡編程、局域網網絡通訊編程、IE編程、網絡通訊協議編程、串口通訊編程、代理server編程和高級網絡通訊編程.
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
開源server
Apache
網遊server開源框架
GNE,HawkNL,RakNet,SDL_net
網絡協議分析軟件:
Sniffer工具
Wireshark 開源的經典的協議分析工具Wireshark, http://www.wireshark.org/
WPE -------抓包
Ethereal -------協議分析
SockMon5 -------抓包及錯誤分析
Windows網絡編程細節問題:
1. 假設在已經處於 ESTABLISHED狀態下的socket(通常由port號和標誌符區分)調用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鏈接server過程當中,假設處於非堵塞模式下的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()設置了緩衝區大小,但還不能知足數據的傳輸需求,通常習慣是本身寫個處理網絡緩衝的類,動態分配內存。
12、#include <Afxsock.h>,#include<winsock2.h>衝突問題
解決方法:在StdAfx.h 頭文件里加入winsock2.h,Afxsock.h
先#include <winsock2.h> 再#include <Afxsock.h>
13、獲取數據包,通常來講想獲取數據包可以使用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.
14、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模型提供了讀寫數據能力的異步通知,但他們不提供異步數據傳送,而重疊及完畢port提供異步數據的傳送。
WSAEventSelect 模型以時間爲基礎的網絡事件通知,但是與WSAAsyncSelect不一樣的是,它主要是由事件對象句柄完畢的,而不是經過窗體。但是一個線程僅僅能等待64個事件(需要開闢多個線程解決),伸縮性不如完成port。
重疊模型可以使程序能達到更佳的系統性能。基本設計原理就是讓應用程序使用重疊的數據結構,一次投遞一個或多個I/O請求。針對這些提交的請求,在他們完畢以後,應用程序可爲他們提供服務。它又分爲兩種實現方法:事件通知和完畢例程。重疊I/O模型事件通知依賴於等待事件通知的線程數(WSAWaitForMultipleEvents調用的每個線程,該I/O模型一次最多都僅僅能支持6 4個套接字。),處理客戶通訊時,大量線程上下文的切換是它們共同的制約因素。
完畢port提供了最好的伸縮性,每每可以使系統達到最好的性能,是處理成千上萬的套接字的首選。從本質上說,完畢port模型要求建立一個windows完畢port對象,該對象經過指定數量的線程,對重疊I/O請求進行管理,以便爲已經完畢的重疊I/O請求提供服務。但是完畢port僅僅是支持NT系統、WIN2000系統。
重疊模型和完畢port模型的應用程序通知緩衝區收發系統直接使用數據,也就是說,假設應用程序投遞了一個10KB大小的緩衝區來接收數據,且數據已經到達套接字,則該數據將直接被複制到投遞的緩衝區。 而select模型、WSAAsyncSelect 模型、WSAEventSelect 模型,數據到達並複製到單套接字接收緩衝區中,此時應用程序會被告知可以讀入的容量。當應用程序調用接收函數以後,數據才從單套接字緩衝區複製到應用程序的緩衝區,區別就體現出來了。
1六、server與clientIO模型選擇
對於怎樣挑選最適合本身應用程序的I/O模型已經很是明晰了。同開發一個簡單的執行多線程的鎖定模式應用相比,其它每種I/O模型都需要更爲複雜的編程工做。所以,針對客戶機和server應用開發模型的選擇,有下面原則。
1). client
若打算開發一個客戶機應用,令其同一時候管理一個或多個套接字,那麼建議採用重疊I/O或WSAEventSelect模型
,以便在必定程度上提高性能。然而,假如開發的是一個以Windows爲基礎的應用程序,要進行窗體消息的管理,那麼WSAAsyncSelect模型恐怕是一種最好的選擇,因爲WSAAsyncSelect自己即是從Windows消息模型借鑑來的。採用這樣的模型,程序需具有消息處理功能。
2). server端
若開發的是一個server應用,要在一個給定的時間,同一時候控制多個套接字,建議採用重疊I/O模型,這相同是從性能角度考慮的。但是,假設server在不論什麼給定的時間,都會爲大量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 , 很不easy被發現。假設是以 Create 方法初始化的前提下不能再調用 Bind ,要不必定出錯。通常寫server程序都不要用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函數可以用來進行網絡字節和主機字節的轉換。
26、怎樣查詢port被佔用的程序
你們在啓動server時,有時正常啓動有時又啓動不了是怎麼回事呢??那爲何關掉迅雷等軟件就又好了呢??現在就來給你們解說一下,
這些port假設被其它程序佔用就不能正常啓動,比方有時啓動時會提示WEB啓動失敗,事實上就是80port被佔用了,而迅雷等下載軟件偏偏就是佔用了80port,關掉便可了。但有時迅雷等都沒有開也啓動不了,那就是別的東西佔用了,那怎麼辦呢?我來叫你查看port並關掉的方法。
1.在開始--執行 裏面輸入cmd點回車,會出現執行窗體。
2.在提示符後輸入netstat -ano回車,找到tcp 80port相應的pid,比方1484.
3.ctrl+alt+del打開任務管理器,選進程,這裏有很是多正在執行的程序怎麼找?別急點上面的 查看--選擇列--在PID(進程標示符)前面打鉤。好了,如下的進程前面都有了PID號碼。這時上一步找到的PID就實用了,找到1484,比方PEER.EXE什麼的,結束進程吧。這時再開server,看WEB可以啓動了!