MFC下CSocket编程详解
1. 常用的函數(shù)和注意事項(xiàng)(詳細(xì)的函數(shù)接口說(shuō)明請(qǐng)查看MSDN):
??? CSocket::Create 初始化(一般寫(xiě)服務(wù)器程序都不要用為好,用下面的 CSocket::Socket 初始化)
?? CSocket::Socket初始化
??? CSocket::SetSockOpt 設(shè)置socket選項(xiàng)
??? CSocket::Bind 綁定地址端口
??? CSocket::Connect 連接
??? CSocket::Listen? 監(jiān)聽(tīng)
??? CSocket::Accept 接收外部連接的socket
?
??? CSocket::Send 發(fā)送內(nèi)容
??? CSocket::Receive 接收內(nèi)容
??? CSocket::Close 關(guān)閉(不等于delete)
??? 1) 在使用MFC編寫(xiě)socket程序時(shí),必須要包含<afxsock.h>都文件。
??? 2) AfxSocketInit() 這個(gè)函數(shù),在使用CSocket前一定要先調(diào)用該函數(shù),否則使用CSocket會(huì)出錯(cuò);并且該函數(shù)還有一個(gè)重要的使用方式,
?????? 就是在某個(gè)線程下使用 CSocket 前一定要調(diào)用,就算主線程調(diào)用了該函數(shù),在子線程下使用 CSocket 也要先調(diào)用該函數(shù),要不會(huì)出錯(cuò)。
??? 3) 還要注意的是, Create 方法已經(jīng)包含了 Bind 方法,如果是以 Create 方法初始化的前提下不能再調(diào)用 Bind ,要不一定出錯(cuò)。
2. 以下是使用例子代碼,通過(guò)例子來(lái)學(xué)習(xí)如何使用 CSocket 進(jìn)行編程, 并且附件上有完整的例子代碼。例子的可以在我的發(fā)布資源中找到:MFC下CSocket編程例子?http://download.csdn.net/source/379597
??? 1) 客戶端主要代碼:
?//初始化?AfxSocketInit();
??
????????//創(chuàng)建?CSocket?對(duì)象
?CSocket?aSocket;
?CString?strIP;
?CString?strPort;
?CString?strText;
?this->GetDlgItem(IDC_EDIT_IP)->GetWindowText(strIP);
?this->GetDlgItem(IDC_EDIT_PORT)->GetWindowText(strPort);
?this->GetDlgItem(IDC_EDIT_TEXT)->GetWindowText(strText);
?//初始化?CSocket?對(duì)象,?因?yàn)榭蛻舳瞬恍枰壎ㄈ魏味丝诤偷刂??所以用默認(rèn)參數(shù)即可
?if(!aSocket.Create())
?{
??char?szMsg[1024]?=?{0};
??sprintf(szMsg,?"create?faild:?%d",?aSocket.GetLastError());
??AfxMessageBox(szMsg);
??return;
?}
?//轉(zhuǎn)換需要連接的端口內(nèi)容類型
?int?nPort?=?atoi(strPort);
?
????????//連接指定的地址和端口
?if(aSocket.Connect(strIP,?nPort))
?{
??char?szRecValue[1024]?=?{0};
????????????????//發(fā)送內(nèi)容給服務(wù)器
??aSocket.Send(strText,?strText.GetLength());
??
??//接收服務(wù)器發(fā)送回來(lái)的內(nèi)容(該方法會(huì)阻塞,?在此等待有內(nèi)容接收到才繼續(xù)向下執(zhí)行)
??aSocket.Receive((void?*)szRecValue,?1024);
??AfxMessageBox(szRecValue);
?}
?else
?{
??char?szMsg[1024]?=?{0};
??
??sprintf(szMsg,?"create?faild:?%d",?aSocket.GetLastError());
??
??AfxMessageBox(szMsg);
?}
?//關(guān)閉
?aSocket.Close();
?
??? 2)服務(wù)器端代碼:
?
unsigned?int?StartServer(LPVOID?lParam){ ????????//初始化Winscok
????if?(!AfxSocketInit())
????{
????????AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
????????return?1;
????}
????m_exit?=?false;
????CServerDlg?*aDlg?=?(CServerDlg?*)lParam;
????CString?strPort;
????
????aDlg->GetDlgItemText(IDC_EDIT_PORT,?strPort);
????
????UINT?nPort?=?atoi(strPort);
????
????//socket------------------------------------------------
????
????CSocket?aSocket,?serverSocket;
????//最好不要使用aSocket.Create創(chuàng)建,因?yàn)槿菀讜?huì)出現(xiàn)10048錯(cuò)誤
????if?(!aSocket.Socket())
????{
????????char?szError[256]?=?{0};
????????
????????sprintf(szError,?"Create?Faild:?%d",?GetLastError());
????????
????????AfxMessageBox(szError);
????????
????????return?1;?
????}
????BOOL?bOptVal?=?TRUE;
????int?bOptLen?=?sizeof(BOOL);
?????//設(shè)置Socket的選項(xiàng), 解決10048錯(cuò)誤必須的步驟
????aSocket.SetSockOpt(SO_REUSEADDR,?(void?*)&bOptVal,?bOptLen,?SOL_SOCKET); ?//監(jiān)聽(tīng)
????if(!aSocket.Listen(10))
????{????
????????char?szError[256]?=?{0};
????????
????????sprintf(szError,?"Listen?Faild:?%d",?GetLastError());
????????
????????AfxMessageBox(szError);
????????
????????return?1;
????}
????
????CString?strText;
????
????aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
????
????strText?+=?"Server?Start!??";
????
????aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
????while(!m_exit)
????{
????????//接收外部連接
????????if(!aSocket.Accept(serverSocket))
????????{
????????????continue;
????????}
????????else
????????{
????????????char?szRecvMsg[256]?=?{0};
????????????char?szOutMsg[256]?=?{0};????
???????????? //接收客戶端內(nèi)容:阻塞
????????????serverSocket.Receive(szRecvMsg,?256);
????????????sprintf(szOutMsg,?"Receive?Msg:?%s??",?szRecvMsg);
????????????
????????????aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
????????????
????????????strText?+=?szOutMsg;
????????????
????????????aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
????? //發(fā)送內(nèi)容給客戶端
????????????serverSocket.Send("Have?Receive?The?Msg",?50);
//關(guān)閉
????????????serverSocket.Close();
????????}
????????
????}
???? //關(guān)閉
????aSocket.Close();
????serverSocket.Close();
????
????aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
????
????strText?+=?"Have?Close!";
????
????aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
????return?0;
}
????
????//綁定端口
????if?(!aSocket.Bind(nPort))
????{
????????char?szError[256]?=?{0};
????????????
????????sprintf(szError,?"Bind?Faild:?%d",?GetLastError());
????????????
????????AfxMessageBox(szError);
????????????
????????return?1;?
????}
??
?
?? 3) SDK 下的服務(wù)器端代碼
?
???????//子線程函數(shù)???????unsigned?int?StartServer(LPVOID?lParam)
???????{
????????
???????//初始化Winsock,?AfxSocketInit()?也是封裝了這些語(yǔ)句,?不過(guò)?AfxSocketInit()?所做的事比這里多些
????WSADATA?wsaData;
???
???????????//Winsock?的版本,?建議用1.1?,兼容性好
????WORD?wVersionRequested?=?MAKEWORD(1,?1);
????int?nResult?=?WSAStartup(wVersionRequested,?&wsaData);
????if?(nResult?!=?0)
????{
??return?1;
????}
???????//-----------------------------------------------------?
???????????m_exit?=?false;
????CServerDlg?*aDlg?=?(CServerDlg?*)lParam;
????CString?strPort;
?
????aDlg->GetDlgItemText(IDC_EDIT_PORT,?strPort);
?
????UINT?nPort?=?atoi(strPort);
?
?//socket------------------------------------------------
???????????
???????????//接口對(duì)象
???????????SOCKET?aSocket,?serverSocket;
???????????//尋址相關(guān)結(jié)構(gòu)
????sockaddr_in?serverSockaddr;
????memset(&serverSockaddr,?0,?sizeof(serverSockaddr));
????aSocket?=?socket(AF_INET,?SOCK_STREAM,?IPPROTO_TCP);
????if?(aSocket?==?INVALID_SOCKET)
????{
??char?szError[256]?=?{0};
??
??sprintf(szError,?"Create?Faild:?%d",?GetLastError());
??
??AfxMessageBox(szError);
??
??return?1;?
????}
????//注意,該處非常重要,取值的正確與否決定關(guān)閉scoket后端口是否能正常釋放
????BOOL?bOptVal?=?TRUE;
????int?bOptLen?=?sizeof(BOOL);
???????????
????????????//設(shè)置?socket?選項(xiàng),?SOL_SOCKET?和?SO_REUSEADDR?一起使用,?并且后面的參數(shù)如上,?
??????????????關(guān)閉scoket后端口便能正常釋放
?????setsockopt(aSocket,?SOL_SOCKET,?SO_REUSEADDR,?(char?*)&bOptVal,?bOptLen);?
?
???????????//尋址相關(guān)結(jié)構(gòu)
????sockaddr_in?aSockaddr;
????memset(&aSockaddr,0,sizeof(aSockaddr));
????aSockaddr.sin_family?=?AF_INET;
????aSockaddr.sin_addr.s_addr?=?htonl(INADDR_ANY);
?
????aSockaddr.sin_port?=?htons((u_short)nPort);
?
???????????//綁定:?注意參數(shù)的類型轉(zhuǎn)換
????if(bind(aSocket,(sockaddr?*)&aSockaddr,?sizeof(aSockaddr))?==?SOCKET_ERROR)
????{
??char?szError[256]?=?{0};
??
??sprintf(szError,?"Bind?Faild:?%d",?GetLastError());
??
??AfxMessageBox(szError);
??
??return?1;?
????}
?
????//監(jiān)聽(tīng)
????if(listen(aSocket,?10)?==?SOCKET_ERROR)
????{?
??char?szError[256]?=?{0};
??
??sprintf(szError,?"Listen?Faild:?%d",?GetLastError());
??
??AfxMessageBox(szError);
??
??return?1;
????}
?
????CString?strText;
????aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
????strText?+=?"Server?Start!??";
????aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
????while(!m_exit)
????{
??//接收外部連接,?非阻塞
??serverSocket?=?accept(aSocket,?(sockaddr?*)&serverSockaddr,?0);
???
??if(serverSocket?==?INVALID_SOCKET)
??{
???continue;
??}
??else
??{
???char?szRecvMsg[256]?=?{0};
???char?szOutMsg[256]?=?{0};?
???
???//接收客戶端內(nèi)容:?阻塞
???recv(serverSocket,?szRecvMsg,?256,?0);
???sprintf(szOutMsg,?"Receive?Msg:?%s??",?szRecvMsg);
???
???aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
???
???strText?+=?szOutMsg;
???aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
????????????????????????//發(fā)送內(nèi)容給客戶端
???send(serverSocket,?"Have?Receive?The?Msg",?50,?0);
????????????????????????//關(guān)閉
???closesocket(serverSocket);
??}
??
????}
?
????//關(guān)閉
????closesocket(aSocket);
????closesocket(serverSocket);
????aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
?
????strText?+=?"Have?Close!";
????aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
???????????//當(dāng)你使用完Winsock接口后,要調(diào)用下面的函數(shù)對(duì)其占用的資源進(jìn)行釋放
????WSACleanup();
????return?0;
???????}
3. 總結(jié)
?? 1) MFC進(jìn)行編程的確比較簡(jiǎn)單, 用的代碼比較少, 又容易管理。唯一不好的地方在于很多細(xì)節(jié)上的東西在資料上不容易查出來(lái), 關(guān)聯(lián)性非常緊密, 象 AfxSocketInit() 函數(shù)就是,函數(shù)的實(shí)現(xiàn)里包含著很多不容易理解的類, 并且記錄了非常多的環(huán)境信息, 比如創(chuàng)建的線程等等, 這樣在主線程調(diào)用后子線程沒(méi)有調(diào)用執(zhí)行 CSocket 的操作就會(huì)出錯(cuò)。還有就是有些接口的設(shè)計(jì)非常離奇, 象 CSocket::Create 的接口就是, 實(shí)現(xiàn)上還執(zhí)行了 CSocket::Bind , 非常不容易被發(fā)現(xiàn)。并且MSDN上對(duì) CSocket::Bind 的說(shuō)明又明顯的提示需要顯示執(zhí)行 CSocket::Bind 操作。
?? 2) SDK 編程能理解函數(shù)的調(diào)用順序和代碼的結(jié)構(gòu)就比較容易,省去了MFC下封裝了不知道什么東西的部分,使得代碼的流程容易控制。但是從上面的例子來(lái)看非常明顯的并且不是那么容易理解。不僅僅有很多奇怪的結(jié)構(gòu)(微軟的命名一直如此, 無(wú)所云云), 并且函數(shù)相關(guān)太過(guò)于緊密, 初學(xué)者想一下子熟悉使用并不容易, 對(duì)開(kāi)發(fā)者來(lái)說(shuō)代碼管理起來(lái)非常麻煩。
總結(jié)
以上是生活随笔為你收集整理的MFC下CSocket编程详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: MFC六大核心机制之五、六:消息映射和命
- 下一篇: MFC中的CAsyncSocket类实现