基於MFC的socket通訊——服務端與客戶端編程

都說這個很簡單,我真的是找了好久然後自己也寫了好久的程序終於,把兩個程序調通了,原理我就不介紹了,大家一搜全是的,在這裏主要是爲了給自己做個記錄也爲了讓廣大的同行們節約時間,不用自己再調代碼。

(1)首先,建立兩個基於對話框的項目,分別命名爲cFile,sFile,注意勾選Windows套接字選項:

 (2)服務端界面如下:插入一個Listbox Control,改ID爲IDC_LIST1,添加變量m_listwords;插入一個Edit Control,ID爲IDC_EDIT1,插入一個Button Control,ID爲IDC_BtnSend,然後添加代碼。

(3)服務端程序:

// sFileDlg.h : 頭文件
//

#pragma once
#include "afxwin.h"

static CString IP;  //定義爲全局變量
void CString2Char(CString str, char ch[]);  //此函數爲字符格式轉換函數

// CsFileDlg 對話框
class CsFileDlg : public CDialogEx
{
// 構造
public:
	CsFileDlg(CWnd* pParent = NULL);	// 標準構造函數

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

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


// 實現
protected:
	HICON m_hIcon;

	// 生成的消息映射函數
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CListBox m_listwords;
	void update(CString s);

private:
	CEdit* show_edit;
	CEdit* send_edit;
	
public:
	afx_msg void OnBnClickedBtnsend();
};

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

#include "stdafx.h"
#include "sFile.h"
#include "sFileDlg.h"
#include "afxdialogex.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


SOCKET listen_sock;
SOCKET sock;

UINT server_thd(LPVOID p);

// 用於應用程序“關於”菜單項的 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()


// CsFileDlg 對話框



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

void CsFileDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST1, m_listwords);
}

BEGIN_MESSAGE_MAP(CsFileDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BtnSend, &CsFileDlg::OnBnClickedBtnsend)
END_MESSAGE_MAP()


// CsFileDlg 消息處理程序

BOOL CsFileDlg::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:  在此添加額外的初始化代碼

	send_edit = (CEdit *)GetDlgItem(IDC_EDIT1);
	send_edit->SetFocus();
	char name[128];
	hostent* pHost;
	gethostname(name, 128);//獲得主機名 
	pHost = gethostbyname(name);//獲得主機結構 
	IP = inet_ntoa(*(in_addr *)pHost->h_addr);
	update(_T("本服務器IP地址:") + IP);
	AfxBeginThread(server_thd, NULL);//創建線程

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

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

// 如果向對話框添加最小化按鈕,則需要下面的代碼
//  來繪製該圖標。  對於使用文檔/視圖模型的 MFC 應用程序,
//  這將由框架自動完成。

void CsFileDlg::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 CsFileDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CsFileDlg::update(CString s)
{
	m_listwords.AddString(s);
}

void CString2Char(CString str, char ch[])//此函數就是字符轉換函數的實現代碼
{
	int i;
	char *tmpch;
	int wLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);//得到Char的長度
	tmpch = new char[wLen + 1];                                             //分配變量的地址大小
	WideCharToMultiByte(CP_ACP, 0, str, -1, tmpch, wLen, NULL, NULL);       //將CString轉換成char*

	for (i = 0; tmpch[i] != '\0'; i++) ch[i] = tmpch[i];
	ch[i] = '\0';
}

UINT server_thd(LPVOID p)//線程要調用的函數
{
	WSADATA wsaData;
	WORD wVersion;
	wVersion = MAKEWORD(2, 2);
	WSAStartup(wVersion, &wsaData);
	// WSAStartup(0x0202, &wsaData);
	SOCKADDR_IN local_addr;
	SOCKADDR_IN client_addr;
	int iaddrSize = sizeof(SOCKADDR_IN);
	int res;
	char msg[1024];
	CsFileDlg * dlg = (CsFileDlg *)AfxGetApp()->GetMainWnd();
	char ch_ip[20];
	CString2Char(IP, ch_ip);//注意!這裏調用了字符格式轉換函數,此函數功能:CString類型轉換爲Char類型,實現代碼在後面添加
	//local_addr.sin_addr.s_addr = htonl(INADDR_ANY);//獲取任意IP地址
	local_addr.sin_addr.s_addr = inet_addr(ch_ip);
	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(8888);
	if ((listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)//創建套接字
	{
		dlg->update(_T("創建監聽失敗"));
	}
	if (bind(listen_sock, (struct sockaddr*) &local_addr, sizeof(SOCKADDR_IN)))//綁定套接字
	{
		dlg->update(_T("綁定錯誤"));
	}
	listen(listen_sock, 1);
	if ((sock = accept(listen_sock, (struct sockaddr *)&client_addr, &iaddrSize)) == INVALID_SOCKET)//接收套接字
	{
		dlg->update(_T("accept 失敗"));
	}
	else
	{
		CString port;
		port.Format(_T("%d"), int(ntohs(client_addr.sin_port)));
		dlg->update(_T("已連接客戶端:") + CString(inet_ntoa(client_addr.sin_addr)) + "  端口:" + port);
	}
	////////////接收數據
	while (1)
	{
		if ((res = recv(sock, msg, 1024, 0)) == -1)
		{
			dlg->update(_T("失去客戶端的連接"));
			break;
		}
		else
		{
			msg[res] = '\0';
			dlg->update(_T("client:") + CString(msg));
		}
	}
	return 0;
}

void CsFileDlg::OnBnClickedBtnsend()
{
	// TODO:  在此添加控件通知處理程序代碼

	CString s;
	char msg[1024];
	send_edit->GetWindowTextW(s);
	CString2Char(s, msg);            //注意!這裏調用了字符格式轉換函數,此函數功能:CString類型轉換爲Char類型,實現代碼後面添加
	if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR)
	{
		show_edit->ReplaceSel(_T("發送失敗"));
		m_listwords.SetWindowTextW(_T("發送失敗"));
	}
	else if (s == "")
	{
		MessageBox(_T("請輸入信息"));
	}
	else
	{
		s = msg;
		//update(s);//消息上屏,清空輸入,並重獲焦點
		//show_edit->ReplaceSel(_T("server:") + s);//消息上屏,清空輸入,並重獲焦點
		m_listwords.AddString(_T("server:") + s);
		send_edit->SetWindowText(_T(""));
		m_listwords.SetFocus();
	}
}

 (4)客戶端界面如下:添加一個IP Control,ID爲IDC_IPADDRESS2,添加變量m_ip;添加一個Listbox Control,ID爲IDC_LIST1,添加變量m_listwords;添加一個Edit Control,ID爲IDC_EDIT1,添加變量m_message;添加兩個按鈕發送和連接,改ID爲IDC_BtnSend和IDC_BtnConnect。

(5)客戶端程序:


// cFileDlg.h : 頭文件
//

#pragma once
#include "afxwin.h"
#include "afxcmn.h"

//void CString2Char(CString str, char ch[]);

// CcFileDlg 對話框
class CcFileDlg : public CDialogEx
{
// 構造
public:
	CcFileDlg(CWnd* pParent = NULL);	// 標準構造函數

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

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


// 實現
protected:
	HICON m_hIcon;

	// 生成的消息映射函數
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CListBox m_listwords;
	CIPAddressCtrl m_ip;
	void update(CString s);
private: 
	CEdit* send_edit;
	CEdit* show_edit;
	CEdit* ip_edit;
public:
	afx_msg void OnBnClickedBtnconnect();
	afx_msg void OnBnClickedBtnsend();
};

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

#include "stdafx.h"
#include "cFile.h"
#include "cFileDlg.h"
#include "afxdialogex.h"
#include <winsock2.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

SOCKET sock;
UINT server_thd(LPVOID p);

// 用於應用程序“關於”菜單項的 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()


// CcFileDlg 對話框



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

void CcFileDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST1, m_listwords);
	DDX_Control(pDX, IDC_IPADDRESS2, m_ip);
}

BEGIN_MESSAGE_MAP(CcFileDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BtnConnect, &CcFileDlg::OnBnClickedBtnconnect)
	ON_BN_CLICKED(IDC_BtnSend, &CcFileDlg::OnBnClickedBtnsend)
END_MESSAGE_MAP()


// CcFileDlg 消息處理程序

BOOL CcFileDlg::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:  在此添加額外的初始化代碼
	send_edit = (CEdit *)GetDlgItem(IDC_EDIT1);
	show_edit = (CEdit *)GetDlgItem(IDC_LIST1);
	ip_edit = (CEdit *)GetDlgItem(IDC_IPADDRESS2);
	return TRUE;  // 除非將焦點設置到控件,否則返回 TRUE
}

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

// 如果向對話框添加最小化按鈕,則需要下面的代碼
//  來繪製該圖標。  對於使用文檔/視圖模型的 MFC 應用程序,
//  這將由框架自動完成。

void CcFileDlg::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 CcFileDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CcFileDlg::update(CString s)
{
	m_listwords.AddString(s);
}

UINT recv_thd(LPVOID p)
{
	int res;
	char msg[1024];
	CcFileDlg * dlg = (CcFileDlg *)AfxGetApp()->GetMainWnd();
	////////////接收數據
	while (1)
	{
		if ((res = recv(sock, msg, 1024, 0)) == -1)//接收服務器的數據
		{
			dlg->update(_T("失去連接"));
			break;
		}
		else
		{
			msg[res] = '\0';
			dlg->update(_T("server:") + CString(msg));
		}
	}
	//closesocket(sock);
	return 0;
}


void CcFileDlg::OnBnClickedBtnconnect()
{
	// TODO:  在此添加控件通知處理程序代碼
	WSADATA wsaData;
	SOCKADDR_IN server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	WORD wVersion;
	wVersion = MAKEWORD(2, 2);
	WSAStartup(wVersion, &wsaData);

	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
	{
		update(_T("create socket error !!!"));
	}
	BYTE nArrIP[4];
	m_ip.GetAddress(nArrIP[0], nArrIP[1], nArrIP[2], nArrIP[3]);
	CString str;
	str.Format(_T("%d.%d.%d.%d"), nArrIP[0], nArrIP[1], nArrIP[2], nArrIP[3]);
	ip_edit->SetWindowText(str);
	char cp[50];
	strncpy(cp, (LPCTSTR)str, sizeof(cp));
	server_addr.sin_addr.s_addr = inet_addr(cp);
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(8888);
	bind(sock, (SOCKADDR*)&server_addr, sizeof(SOCKADDR));
	if (connect(sock, (struct sockaddr *) &server_addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
	{
		update(_T("連接失敗"));
	}
	else
	{
		//show_edit->SetWindowText(_T(""));
		update(_T("連接成功"));
		//btnconnet->EnableWindow(FALSE);//按鈕變灰
		AfxBeginThread(recv_thd, NULL);
	}
	}


void CcFileDlg::OnBnClickedBtnsend()
{
	// TODO:  在此添加控件通知處理程序代碼
	CString s;
	char *msg;
	send_edit->GetWindowText(s);
	//CString2Char(s, msg);
	//strncpy(msg, (LPCTSTR)s, sizeof(msg));
	msg = s.GetBuffer(s.GetLength());
	if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR)
	{
		update(_T("發送失敗"));
	}
	else if (s == "")
	{
		MessageBox(_T("請輸入信息"));
	}
	else
	{
		s = msg;
		update(_T("client:") + s);//消息上屏,清空輸入,並重獲焦點
		send_edit->SetWindowText(_T(""));
		send_edit->SetFocus();
	}
}

(6)結果:

 

發佈了51 篇原創文章 · 獲贊 40 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章