MFC下CSocket编程详解
1. 常用的函數和注意事項(詳細的函數接口說明請查看MSDN):
??? CSocket::Create 初始化(一般寫服務器程序都不要用為好,用下面的 CSocket::Socket 初始化)
?? CSocket::Socket初始化
??? CSocket::SetSockOpt 設置socket選項
??? CSocket::Bind 綁定地址端口
??? CSocket::Connect 連接
??? CSocket::Listen? 監聽
??? CSocket::Accept 接收外部連接的socket
?
??? CSocket::Send 發送內容
??? CSocket::Receive 接收內容
??? CSocket::Close 關閉(不等于delete)
??? 1) 在使用MFC編寫socket程序時,必須要包含<afxsock.h>都文件。
??? 2) AfxSocketInit() 這個函數,在使用CSocket前一定要先調用該函數,否則使用CSocket會出錯;并且該函數還有一個重要的使用方式,
?????? 就是在某個線程下使用 CSocket 前一定要調用,就算主線程調用了該函數,在子線程下使用 CSocket 也要先調用該函數,要不會出錯。
??? 3) 還要注意的是, Create 方法已經包含了 Bind 方法,如果是以 Create 方法初始化的前提下不能再調用 Bind ,要不一定出錯。
2. 以下是使用例子代碼,通過例子來學習如何使用 CSocket 進行編程, 并且附件上有完整的例子代碼。例子的可以在我的發布資源中找到:MFC下CSocket編程例子?http://download.csdn.net/source/379597
??? 1) 客戶端主要代碼:
?//初始化?AfxSocketInit();
??
????????//創建?CSocket?對象
?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?對象,?因為客戶端不需要綁定任何端口和地址,?所以用默認參數即可
?if(!aSocket.Create())
?{
??char?szMsg[1024]?=?{0};
??sprintf(szMsg,?"create?faild:?%d",?aSocket.GetLastError());
??AfxMessageBox(szMsg);
??return;
?}
?//轉換需要連接的端口內容類型
?int?nPort?=?atoi(strPort);
?
????????//連接指定的地址和端口
?if(aSocket.Connect(strIP,?nPort))
?{
??char?szRecValue[1024]?=?{0};
????????????????//發送內容給服務器
??aSocket.Send(strText,?strText.GetLength());
??
??//接收服務器發送回來的內容(該方法會阻塞,?在此等待有內容接收到才繼續向下執行)
??aSocket.Receive((void?*)szRecValue,?1024);
??AfxMessageBox(szRecValue);
?}
?else
?{
??char?szMsg[1024]?=?{0};
??
??sprintf(szMsg,?"create?faild:?%d",?aSocket.GetLastError());
??
??AfxMessageBox(szMsg);
?}
?//關閉
?aSocket.Close();
?
??? 2)服務器端代碼:
?
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創建,因為容易會出現10048錯誤
????if?(!aSocket.Socket())
????{
????????char?szError[256]?=?{0};
????????
????????sprintf(szError,?"Create?Faild:?%d",?GetLastError());
????????
????????AfxMessageBox(szError);
????????
????????return?1;?
????}
????BOOL?bOptVal?=?TRUE;
????int?bOptLen?=?sizeof(BOOL);
?????//設置Socket的選項, 解決10048錯誤必須的步驟
????aSocket.SetSockOpt(SO_REUSEADDR,?(void?*)&bOptVal,?bOptLen,?SOL_SOCKET); ?//監聽
????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};????
???????????? //接收客戶端內容:阻塞
????????????serverSocket.Receive(szRecvMsg,?256);
????????????sprintf(szOutMsg,?"Receive?Msg:?%s??",?szRecvMsg);
????????????
????????????aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
????????????
????????????strText?+=?szOutMsg;
????????????
????????????aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
????? //發送內容給客戶端
????????????serverSocket.Send("Have?Receive?The?Msg",?50);
//關閉
????????????serverSocket.Close();
????????}
????????
????}
???? //關閉
????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 下的服務器端代碼
?
???????//子線程函數???????unsigned?int?StartServer(LPVOID?lParam)
???????{
????????
???????//初始化Winsock,?AfxSocketInit()?也是封裝了這些語句,?不過?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------------------------------------------------
???????????
???????????//接口對象
???????????SOCKET?aSocket,?serverSocket;
???????????//尋址相關結構
????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;?
????}
????//注意,該處非常重要,取值的正確與否決定關閉scoket后端口是否能正常釋放
????BOOL?bOptVal?=?TRUE;
????int?bOptLen?=?sizeof(BOOL);
???????????
????????????//設置?socket?選項,?SOL_SOCKET?和?SO_REUSEADDR?一起使用,?并且后面的參數如上,?
??????????????關閉scoket后端口便能正常釋放
?????setsockopt(aSocket,?SOL_SOCKET,?SO_REUSEADDR,?(char?*)&bOptVal,?bOptLen);?
?
???????????//尋址相關結構
????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);
?
???????????//綁定:?注意參數的類型轉換
????if(bind(aSocket,(sockaddr?*)&aSockaddr,?sizeof(aSockaddr))?==?SOCKET_ERROR)
????{
??char?szError[256]?=?{0};
??
??sprintf(szError,?"Bind?Faild:?%d",?GetLastError());
??
??AfxMessageBox(szError);
??
??return?1;?
????}
?
????//監聽
????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};?
???
???//接收客戶端內容:?阻塞
???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);
????????????????????????//發送內容給客戶端
???send(serverSocket,?"Have?Receive?The?Msg",?50,?0);
????????????????????????//關閉
???closesocket(serverSocket);
??}
??
????}
?
????//關閉
????closesocket(aSocket);
????closesocket(serverSocket);
????aDlg->GetDlgItemText(IDC_EDIT_LOG,?strText);
?
????strText?+=?"Have?Close!";
????aDlg->SetDlgItemText(IDC_EDIT_LOG,?strText);
???????????//當你使用完Winsock接口后,要調用下面的函數對其占用的資源進行釋放
????WSACleanup();
????return?0;
???????}
3. 總結
?? 1) MFC進行編程的確比較簡單, 用的代碼比較少, 又容易管理。唯一不好的地方在于很多細節上的東西在資料上不容易查出來, 關聯性非常緊密, 象 AfxSocketInit() 函數就是,函數的實現里包含著很多不容易理解的類, 并且記錄了非常多的環境信息, 比如創建的線程等等, 這樣在主線程調用后子線程沒有調用執行 CSocket 的操作就會出錯。還有就是有些接口的設計非常離奇, 象 CSocket::Create 的接口就是, 實現上還執行了 CSocket::Bind , 非常不容易被發現。并且MSDN上對 CSocket::Bind 的說明又明顯的提示需要顯示執行 CSocket::Bind 操作。
?? 2) SDK 編程能理解函數的調用順序和代碼的結構就比較容易,省去了MFC下封裝了不知道什么東西的部分,使得代碼的流程容易控制。但是從上面的例子來看非常明顯的并且不是那么容易理解。不僅僅有很多奇怪的結構(微軟的命名一直如此, 無所云云), 并且函數相關太過于緊密, 初學者想一下子熟悉使用并不容易, 對開發者來說代碼管理起來非常麻煩。
總結
以上是生活随笔為你收集整理的MFC下CSocket编程详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MFC六大核心机制之五、六:消息映射和命
- 下一篇: MFC中的CAsyncSocket类实现