C/S模式
客戶(hù)機/服務(wù)器模式的建立基于以下兩點(diǎn):首先,建立網(wǎng)絡(luò )的起因是網(wǎng)絡(luò )中軟硬件資源、運算能力和信息不均等,需要共享,從而造就擁有眾多資源的主機提供服務(wù),資源較少的客戶(hù)請求服務(wù)這一非對等作用。其次,網(wǎng)間進(jìn)程通信完全是異步的,相互通信的進(jìn)程間既不存在父子關(guān)系,又不共享內存緩沖區,因此需要一種機制為希望通信的進(jìn)程間建立聯(lián)系,為二者的數據交換提供同步,這就是基于客戶(hù)機/服務(wù)器模式的TCP/IP。
客戶(hù)機/服務(wù)器模式在操作過(guò)程中采取的是主動(dòng)請求的方式。
基于TCP(面向連接)的socket編程
服務(wù)端進(jìn)程通過(guò)bind方法將其套接字告知系統,以使其他的套接字能找到它。它可通過(guò)套接字的“偵聽(tīng)(listen)”來(lái)“接收(accept)”發(fā)過(guò)來(lái)的信息??蛻?hù)端的進(jìn)程同服務(wù)端套接字建立連接然后交換信息。需要的信息都可以從該通道向任一端進(jìn)行發(fā)送。
服務(wù)器:
創(chuàng )建端點(diǎn) (socket())
綁定地址(bind())
指定隊列(listen())
等待連接 (accept())
傳輸數據 (read()/write()) 客戶(hù)端:
創(chuàng )建端點(diǎn) (socket())
鏈接服務(wù)器 (connect())
傳輸數據(read()/write())
基于UDP(面向無(wú)連接)的socket編程 用無(wú)連接協(xié)議,雙方的套接字都需要用bind方法來(lái)告知系統。這是因為每方的信息都會(huì )單獨處理,所以每次服務(wù)端發(fā)信息過(guò)來(lái)時(shí),客戶(hù)端都需要找到它,反之亦然。每次調用bind方法,都綁定了一個(gè)新的端口。當然,如果端口已經(jīng)被使用了,則是不能被綁定的。如果你指定的端口為0,則系統會(huì )把當前可用的端口自動(dòng)給你一個(gè)。由于發(fā)送信息的額外任務(wù),進(jìn)程不會(huì )使用read/write方法,而是使用recvfrom/sendto方法。這兩個(gè)方法的參數一個(gè)是要寫(xiě)入的套接字,另一個(gè)則是遠程計算機上服務(wù)的地址。
服務(wù)端:
創(chuàng )建端點(diǎn)(socket())
綁定地址 (bind())
傳輸數據(sendto()/recvfrom())
客戶(hù)端:
創(chuàng )建端點(diǎn) (socket())
綁定地址(bind()) (connect方法可選擇調用)
連接服務(wù)端(connect())
傳輸數據(sendto()/recvfrom())
多線(xiàn)程的設計 1、盡量少的使用全局變量、static變量做共享數據,盡量使用參數傳遞對象。
2、在MFC中請慎用線(xiàn)程。因為MFC的框架假定你的消息處理都是在主線(xiàn)程中完成的。首先窗口句柄是屬于線(xiàn)程的,如果擁有窗口句柄的線(xiàn)程退出了,如果另一個(gè)線(xiàn)程處理這個(gè)窗口句柄,系統就會(huì )出現問(wèn)題。而MFC為了避免這種情況的發(fā)生,使你在子線(xiàn)程中調用消息(窗口)處理函數時(shí),就會(huì )不停的出Assert錯誤,煩都煩死你。典型的例子就時(shí)CSocket,因為CSocket是使用了一個(gè)隱藏窗口實(shí)現了假阻塞,所以不可避免的使用了消息處理函數,如果你在子線(xiàn)程中使用CSocket,你就可能看到assert的彈出了。
3、不要在不同的線(xiàn)程中同時(shí)注冊COM組件。
常見(jiàn)問(wèn)題的解決
1、關(guān)閉套接字
我們在利用IOCP(完成端口)進(jìn)行程序設計的時(shí)候,經(jīng)常要關(guān)閉一些不滿(mǎn)足條件的套接字。假如我們直接采用closesocket方法進(jìn)行關(guān)閉的話(huà),綁定到IO端口的此套接字的未發(fā)送的數據就會(huì )丟失,這種情況是我們不愿意發(fā)生的。下面介紹一種合理關(guān)閉此套接字的方法:
首先,利用setsockopt(MSDN)函數設定套接字的選項,我們把此套接字設定為:假如有數據未發(fā)送,當數據發(fā)送完后再關(guān)閉此套接字。
代碼如下:
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
setsockopt(Socket, SOL_SOCKET, SO_LINGER,
(char *)&lingerStruct, sizeof(lingerStruct) );
// Now close the socket handle. This will do an abortive or graceful close, as requested.
CancelIo((HANDLE) Socket);
closesocket(Socket);
clientSocket = INVALID_SOCKET;
當在完成端口的數據被發(fā)送出去之后,套接字就會(huì )被關(guān)閉,這樣我們就完成了一個(gè)套接字的關(guān)閉。
2、解決 Socket API錯誤代碼:WSAECONNRESET (10054)
出現原因:使用UDP SOCKET時(shí)(利用事件觸發(fā)方式),如果發(fā)送端在發(fā)送數據時(shí)(WSASendTo),接收端沒(méi)還有創(chuàng )建,那么發(fā)送端將會(huì )收到一個(gè)事件通知,此時(shí)調用WSARecv()函數時(shí)將會(huì )產(chǎn)生調用錯誤(WSAECONNRESET ),從這以后,這個(gè)發(fā)送端這個(gè)SOCKET無(wú)法接受到數據。解決辦法:
a.頭文件中加入下面代碼:
#include <Winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define IOC_VENDOR 0x18000000
#define _WSAIOW(x,y) (IOC_IN|(x)|(y))
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
b.在創(chuàng )建socket之后加入下面代碼:
DWORD dwBytesReturned = 0;
BOOL bNewBehavior = FALSE;
DWORD status;
status = WSAIoctl(m_hSock, SIO_UDP_CONNRESET,
&bNewBehavior,
sizeof (bNewBehavior),
NULL, 0, &dwBytesReturned,
NULL, NULL);
<參考
http://blog.csdn.net/wupangzi/archive/2009/07/27/4384081.aspx及
http://hi.baidu.com/jetqu2003/blog/item/397700031435e9703812bbcc.html>