1. 程式人生 > >新手MFC學習之Socket練習

新手MFC學習之Socket練習

sign 做了 tin 避免 click mes accept 否則 continue

事實上MFC這東西吧,好像也不光是MFC,非常多東西,事實上我如今才感覺到,假設想高速做一個東西出來的話。是沒有必要系統的學關於這個東西的所有知識的。比方我想做一個MFC相似QQ的軟件,可是我又不想花太多時間去一本厚厚的VC++, 索性就在網上找了博客,去研究一下。做了一個Socket的小練習,由於之前用Python寫了一個相似的東西,所以在理解起來不是非常困難。僅僅只是我感覺用C++必用Python做這個還要easy一點。除了文件太大了。

話不多說了,以下具體介紹一下我的這個練習。


Scoket呢,就是一個叫做套接字的東西,它是連接兩個通信計算機的橋梁,這個裏面封裝了非常多的函數。能夠用來在計算機之間傳送信息,監聽指定port進來的連接。本次練習用到的是一個叫做CSocket的類。

在初始化對象的時候。有以下幾點是須要註意的:

1、AfxSocketInit() 這個函數是初始化套接字。值得一提的是,無論是在主線程。還是在子線程內,假設你想用到Socket對象的話。那麽一定要在前面先調用這個函數。不然的話就會出錯,並且是每個線程都要調用。

大家在建立MFC項目的時候,那個叫做XXApp.cpp的文件就是整個程序的主線程,沒有這個文件也就沒有了這個程序,之後你能夠利用其它的方法創建子線程。


2、在初始化一個Socket對象的時候,假設你的這個對象是用在服務器端,也就是這個對象到時候要調用一些方法來監聽port連接。並且收發消息的時候,此時不要用Creat這個函數初始化,直接用xx.Socket()這個函數。Creat函數是在創建這個對象的時候,自己主動就包含Bind這個綁定套接字對象到指定port的功能,可是Scoket不會。之所以這麽做就是為了避免一些不必要的錯誤。


3、用到的方法也非常easy,bind綁定套接字到指定port。 listen會監聽指定port的連接。 Accept會接受client發來的連接。


其它的我就不再多說了,我在凝視裏面講了非常多,另一點要告訴大家的是:假設你不懂這種方法是做什麽的,直接選定它然後按f1就會跳到MSDN的文檔界面,裏面講的非常清楚,我也是零基礎。和大家一樣第一次接觸這個東西,我認為這樣的方法是可行的。

http://download.csdn.net/detail/u010092734/7665651我的源代碼在這裏。大家能夠下載。用的VS2012,下載下來直接能夠執行


服務器端代碼:

// ServerDlg.cpp : 實現文件
//

#include "stdafx.h"
#include "Server.h"
#include "ServerDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CWinThread *m_pThread = NULL;            //創建一個線程

// 用於應用程序“關於”菜單項的 CAboutDlg 對話框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 對話框數據
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 實現
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CServerDlg 對話框



CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CServerDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CServerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CServerDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDOK, &CServerDlg::OnBnClickedOk)
	ON_BN_CLICKED(IDCANCEL, &CServerDlg::OnBnClickedCancel)
	ON_BN_CLICKED(IDC_close_BUTTON1, &CServerDlg::OnBnClickedcloseButton1)
END_MESSAGE_MAP()


bool m_exit = false;
unsigned StartServer(LPVOID lParam)
{
	//初始化Winscok
	if (!AfxSocketInit())
	{
		AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
		return 1;
	}
	m_exit = false;
    CString strpoint;			//獲取port號
	CServerDlg *aDlg = (CServerDlg *)lParam;
	aDlg->GetDlgItemText(IDC_port_EDIT1, strpoint);          //獲取輸入框內輸入的內容
	char* p=(char*)strpoint.LockBuffer();                    //將Cstring類型的字符串轉化為char *
	unsigned int nPort = atoi(p);
    CSocket aSocket, serverSocket;                            //創建兩個套接字
	if(!aSocket.Socket())                                    //分配一個 套接字句柄用於以後使用這個套接字
	{
		char ErrorInfo[256] = {0};                           //創建數組存儲錯誤信息
		sprintf(ErrorInfo, "Creat Faile : %d", GetLastError());   //把錯誤信息寫入數組
		AfxMessageBox(ErrorInfo);
		return 1;
	}
	BOOL bOptVal = TRUE;
	int bOptLen = sizeof(BOOL);
	//設置Socket的選項, 解決10048錯誤必須的步驟,感覺像平時做題目開外掛一樣,不寫這些就會報錯
	aSocket.SetSockOpt(SO_REUSEADDR, (void *)&bOptVal, bOptLen, SOL_SOCKET);

	//將套接字綁定到port
	if(!aSocket.Bind(nPort))
	{
		char szError[256] = {0};
		sprintf(szError, "Bind Faild: %d", GetLastError());
		AfxMessageBox(szError);	
		return 1; 
	}

	//開始監聽該port發來的連接
	if(!aSocket.Listen(10))
	{
		char szError[256] = {0};
		sprintf(szError, "Listen Faild: %d", GetLastError());
		AfxMessageBox(szError);	
		return 1; 
	}
	CString strText;
	aDlg->GetDlgItemText(IDC_ok_EDIT2, strText);           //獲取控件的內容
	strText += "服務已經開啟\r\n";
	aDlg->SetDlgItemText(IDC_ok_EDIT2, strText);          //設置控件的內容
	//aDlg->GetDlgItem(IDC_ok_EDIT2)->EnableWindow(false);
	while(!m_exit)
	{
		 if(!aSocket.Accept(serverSocket))    //接受來自client的連接
		 {
			 continue;
		 }
		 else
		 {

			 char ReciveMsg[256] = {0};
			 char SendMsg[256] = {0};
			 serverSocket.Receive(ReciveMsg, 256);
			 sprintf(SendMsg, "接收到的信息是:%s\r\n", ReciveMsg);
			 strText += SendMsg;
			 aDlg->SetDlgItemText(IDC_ok_EDIT2, strText);
			 serverSocket.Send("服務器已經收到client發來的消息。已經做出操作!

", 50); serverSocket.Close(); } } aSocket.Close(); //處理連接完畢,關閉套接字 serverSocket.Close(); aDlg->GetDlgItemText(IDC_ok_EDIT2, strText); strText += "已經關閉!

"; aDlg->SetDlgItemText(IDC_ok_EDIT2, strText); return 0; } void StopServer() { m_exit = true; } // CServerDlg 消息處理程序 BOOL CServerDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 將“關於...”菜單項加入到系統菜單中。 // IDM_ABOUTBOX 必須在系統命令範圍內。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 設置此對話框的圖標。當應用程序主窗體不是對話框時,框架將自己主動 // 執行此操作 SetIcon(m_hIcon, TRUE); // 設置大圖標 SetIcon(m_hIcon, FALSE); // 設置小圖標 // TODO: 在此加入額外的初始化代碼 return TRUE; // 除非將焦點設置到控件,否則返回 TRUE } void CServerDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 假設向對話框加入最小化button,則須要以下的代碼 // 來繪制該圖標。對於使用文檔/視圖模型的 MFC 應用程序, // 這將由框架自己主動完畢。

void CServerDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用於繪制的設備上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使圖標在工作區矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 繪制圖標 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } //當用戶拖動最小化窗體時系統調用此函數取得光標 //顯示。 HCURSOR CServerDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CServerDlg::OnBnClickedOk() { // TODO: 在此加入控件通知處理程序代碼 m_pThread = new CWinThread(StartServer, (LPVOID)this); //帶參數的構造函數和不帶參數的構造函數的差別是,有參數的構造函數直接執行線程函數.不帶參數的直接執行CWinThread::InitInstance函數. m_pThread->CreateThread(CREATE_SUSPENDED); m_pThread->ResumeThread(); SetDlgStatus(true); } void CServerDlg::SetDlgStatus(bool isOpen) { this->GetDlgItem(IDOK)->EnableWindow(!isOpen); this->GetDlgItem(IDC_close_BUTTON1)->EnableWindow(isOpen); this->GetDlgItem(IDC_port_EDIT1)->EnableWindow(!isOpen); } void CServerDlg::OnBnClickedCancel() { // TODO: 在此加入控件通知處理程序代碼 CDialogEx::OnCancel(); } void CServerDlg::OnBnClickedcloseButton1() { // TODO: 在此加入控件通知處理程序代碼 StopServer(); m_pThread->SuspendThread(); delete m_pThread; m_pThread = NULL; SetDlgStatus(false); }


client代碼:


// ClientDlg.cpp : 實現文件
//

#include "stdafx.h"
#include "Client.h"
#include "ClientDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用於應用程序“關於”菜單項的 CAboutDlg 對話框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

	// 對話框數據
	enum { IDD = IDD_ABOUTBOX };

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	// 實現
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CClientDlg 對話框



CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CClientDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CClientDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_SEND_BUTTON1, &CClientDlg::OnBnClickedSendButton1)
END_MESSAGE_MAP()


// CClientDlg 消息處理程序

BOOL CClientDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 將“關於...”菜單項加入到系統菜單中。

	// IDM_ABOUTBOX 必須在系統命令範圍內。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 設置此對話框的圖標。當應用程序主窗體不是對話框時,框架將自己主動
	//  執行此操作
	SetIcon(m_hIcon, TRUE);			// 設置大圖標
	SetIcon(m_hIcon, FALSE);		// 設置小圖標

	// TODO: 在此加入額外的初始化代碼

	return TRUE;  // 除非將焦點設置到控件。否則返回 TRUE
}

void CClientDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 假設向對話框加入最小化button。則須要以下的代碼
//  來繪制該圖標。對於使用文檔/視圖模型的 MFC 應用程序。
//  這將由框架自己主動完畢。

void CClientDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用於繪制的設備上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使圖標在工作區矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 繪制圖標
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//當用戶拖動最小化窗體時系統調用此函數取得光標
//顯示。
HCURSOR CClientDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CClientDlg::OnBnClickedSendButton1()
{
	// TODO: 在此加入控件通知處理程序代碼
	if (!	())                   //初始化CSocket對象
	{
		AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
	}
	
	CSocket ClientSocket;
	CString StrIp, StrPort, StrText;
	this->GetDlgItemText(IDC_Ip_EDIT1, StrIp);            //獲取控件內輸入的內容
	this->GetDlgItemText(IDC_PORT_EDIT2, StrPort);
	this->GetDlgItemText(IDC_TEXT_EDIT3, StrText);

	//初始化SCOKET對象
	if(!ClientSocket.Create())
	{
		char Error[256] = {0};
		sprintf(Error, "Creat SCOKET OBJ is faild: %d", ClientSocket.GetLastError());
		AfxMessageBox(Error);
		return ;
	}

	char* p=(char*)StrPort.LockBuffer();                    //將Cstring類型的字符串轉化為char *
	unsigned int nPort = atoi(p);

	if(!ClientSocket.Connect(StrIp, nPort))
	{
		char Error[256] = {0};
		sprintf(Error, "Connect SCOKET OBJ is faild: %d", ClientSocket.GetLastError());
		AfxMessageBox(Error);
		return ;
	}
	else
	{
		char ReciveInfo[2000] = {0};
		ClientSocket.Send(StrText, StrText.GetLength());    //向服務器發送信息
		ClientSocket.Receive(ReciveInfo, sizeof(ReciveInfo));
		AfxMessageBox(ReciveInfo);
	}
	ClientSocket.Close();
}


新手MFC學習之Socket練習