這是上個學期網絡通訊的課程設計所做的作業,C++語言中的MFC寫的。
後面將更新C#版本的基於socket的聊天室程序。廢話不多說,直接上源碼:
服務端代碼:
// sFileDlg.cpp : 實現文件
//
#include <winsock2.h>
#include "stdafx.h"
#include "sFile.h"
#include "sFileDlg.h"
#include "afxdialogex.h"
#pragma comment(lib, "WS2_32.lib")
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
UINT server_thd(LPVOID p); //聲明線程函數
SOCKET listen_sock;//定義一個全局的監聽soket
SOCKET sock;//定義一個soket
CString showeditmsg; //定義全局的顯示消息的字符串
// CsFileDlg 對話框
CsFileDlg::CsFileDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CsFileDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CsFileDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CsFileDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BtnSend, &CsFileDlg::OnBnClickedBtnsend)
ON_BN_CLICKED(IDC_BUTTON2, &CsFileDlg::OnBnClickedButton2)
END_MESSAGE_MAP()
// CsFileDlg 消息處理程序
BOOL CsFileDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
show_edit = (CEdit *)GetDlgItem(IDC_EDIT1);
send_edit = (CEdit *)GetDlgItem(IDC_EDIT2);
port_edit = (CEdit *)GetDlgItem(IDC_EDIT3);
star_btn = (CButton *)GetDlgItem(IDC_BUTTON2);
showmsg_edit = (CEdit *)GetDlgItem(IDC_EDIT4);
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
// 如果向對話框添加最小化按鈕,則需要下面的代碼
// 來繪製該圖標。對於使用文檔/視圖模型的 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);
}
//添加函數update() 更新消息數據
void CsFileDlg::update(CString s)
{
showeditmsg += s;
show_edit->SetWindowText(showeditmsg);
}
//開啓服務器按鈕響應事件
void CsFileDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知處理程序代碼
WSADATA wsData;
WSAStartup(MAKEWORD(2,2), &wsData);
char name[80];
CString IP;
CString P;
hostent* pHost;
gethostname(name,sizeof(name));//獲得主機名
pHost = gethostbyname(name);//獲得主機結構
IP = inet_ntoa(*(struct in_addr *)pHost->h_addr_list[1]);//獲取主機ip地址
showmsg_edit->SetWindowText("綁定IP地址:" + IP);
AfxBeginThread(&server_thd,0); //開啓新線程處理
}
//發送按鈕響應事件
void CsFileDlg::OnBnClickedBtnsend()
{
// TODO: 在此添加控件通知處理程序代碼
CString s;
char * msg;
send_edit->GetWindowText(s);
msg = (char*)s.GetBuffer(s.GetLength()); //獲取發送的數據的緩衝區的指針
if(s == "")
{
MessageBox("請輸入信息");
}
else if(send(sock, msg, strlen(msg), 0) == SOCKET_ERROR) //發送數據,並判斷是否成功
{
update("發送失敗\r\n");
}
else
{
update("我:" + s + "\r\n");//消息上屏
send_edit->SetWindowText("");//清空輸入
send_edit->SetFocus();//並重獲焦點
}
}
//添加線程函數server_thd():
UINT server_thd(LPVOID p)
{
WSADATA wsaData;
WORD wVersion;
wVersion = MAKEWORD(2,2);
WSAStartup(wVersion,&wsaData);
SOCKADDR_IN local_addr;
SOCKADDR_IN client_addr;
int iaddrSize = sizeof(SOCKADDR_IN);
int res;
char msg[1024];
CString port;
CsFileDlg * dlg = (CsFileDlg *)AfxGetApp()->GetMainWnd(); //得到應用程序活動主窗口的指針
dlg->port_edit->GetWindowText(port);//獲得端口地址
if(port=="")
{
AfxMessageBox("請輸入端口號");
return 0;
}
//爲local_addr賦值,創建soket
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(atoi(port)); //atoi, 把字符串轉換成整型數
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//創建監聽listen_sock
if( (listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET )
{
dlg->update("創建監聽失敗\r\n");
return 0;
}
//綁定
if( bind(listen_sock, (struct sockaddr*) &local_addr, sizeof(SOCKADDR_IN)) )
{
dlg->update("綁定錯誤,換一個端口試試吧~\r\n");
return 0;
}
listen(listen_sock, 1); //開始監聽,允許最大監聽數爲1
dlg->star_btn->EnableWindow(FALSE); //開啓服務器按鈕灰化
dlg->showmsg_edit->ShowWindow(SW_SHOW); //綁定消息edit顯示
dlg->update("已成功開啓....\r\n");
//接受連接請求
if( (sock = accept(listen_sock, (struct sockaddr *)&client_addr, &iaddrSize)) == INVALID_SOCKET)
{
dlg->update("接受連接請求失敗\r\n");
return 0;
}
else
{
CString port;
port.Format("%d", int(ntohs(client_addr.sin_port)));
dlg->update( "已連接來自:" + CString(inet_ntoa(client_addr.sin_addr)) + " 端口:" +
port+"\r\n");
}
////////////接收數據
while(1)
{
if( (res = recv(sock, msg, 1024, 0)) == -1 ) //接收數據,判斷是否接收成功
{
dlg->update("失去連接\r\n");
dlg->star_btn->EnableWindow(TRUE);
break;
}
else
{
dlg->update("客戶端:" + (CString(msg)).Mid(0,res)+"\r\n");
}
}
return 0;
}
客戶端代碼:
// cFileDlg.cpp : 實現文件
//
#include <winsock2.h>
#include "stdafx.h"
#include "cFile.h"
#include "cFileDlg.h"
#include "afxdialogex.h"
#pragma comment(lib, "WS2_32.lib")
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
SOCKET sock; //創建全局的soket
UINT recv_thd(LPVOID p); //聲明線程函數
CString edit1msg; // 定義全局的顯示消息的字符串
// CcFileDlg 對話框
CcFileDlg::CcFileDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CcFileDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CcFileDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CcFileDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_SEND, &CcFileDlg::OnBnClickedSend)
ON_BN_CLICKED(IDC_btnConnect, &CcFileDlg::OnBnClickedbtnconnect)
END_MESSAGE_MAP()
// CcFileDlg 消息處理程序
BOOL CcFileDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 設置此對話框的圖標。當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
//初始化
edit1 = (CEdit *)GetDlgItem(IDC_EDIT1);
send_edit = (CEdit *)GetDlgItem(IDC_EDIT2);
btnconn = (CButton *)GetDlgItem(IDC_btnConnect);
ip_edit = (CEdit *)GetDlgItem(IDC_EDIT3);
port_edit = (CEdit *)GetDlgItem(IDC_EDIT4);
ip_edit->SetWindowText("127.0.0.1");
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
// 如果向對話框添加最小化按鈕,則需要下面的代碼
// 來繪製該圖標。對於使用文檔/視圖模型的 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)
{
edit1msg += s;
edit1->SetWindowText(edit1msg);
}
//鏈接,點連接按鈕執行事件
void CcFileDlg::OnBnClickedbtnconnect()
{
// TODO: 在此添加控件通知處理程序代碼
WSADATA wsaData;
SOCKADDR_IN server_addr;
WORD wVersion;
wVersion = MAKEWORD(2,2);
WSAStartup(wVersion,&wsaData);//使用Socket的程序在使用Socket之前必須調用WSAStartup函數。
//該函數的第一個參數指明程序請求使用的Socket版本,其中高位字節指明副版本、低位字節指明主版本;
//操作系統利用第二個參數返回請求的Socket的版本信息。
//當一個應用程序調用WSAStartup函數時,操作系統根據請求的Socket版本來搜索相應的Socket庫,
//然後綁定找到的Socket庫到該應用程序中。以後應用程序就可以調用所請求的Socket庫中的其它Socket函數了。
//該函數執行成功後返回0
CString ip;
CString port;
ip_edit->GetWindowText(ip);//取得服務器的IP地址
port_edit->GetWindowText(port);//取得服務器的端口
if(port=="") //判斷端口是否爲空
{
AfxMessageBox("請輸入端口號");
btnconn->EnableWindow(TRUE);
return;
}
//爲server_addr賦值 ,創建soket
server_addr.sin_addr.s_addr = inet_addr((LPCSTR)ip);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(port));
if( (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) //判斷soket是否創建成功
{
update("創建Soket錯誤!\r\n");
btnconn->EnableWindow(TRUE);
return;
}
if( connect(sock, (struct sockaddr *) &server_addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)//判斷鏈接是否成功
{
update("連接失敗\r\n");
btnconn->EnableWindow(TRUE);
return;
}
else
{
update("連接成功\r\n");
AfxBeginThread(&recv_thd, 0); //開啓線程處理
btnconn->EnableWindow(FALSE);//連接按鈕變灰
}
}
//發送消息按鈕處理事件
void CcFileDlg::OnBnClickedSend()
{
CString s;
char * msg;
send_edit->GetWindowText(s);
msg = (char*)s.GetBuffer(s.GetLength()); //獲取發送的數據的緩衝區的指針
if(send(sock, msg, strlen(msg), 0) == SOCKET_ERROR) //判斷髮送是否成功
{
update("發送失敗\r\n");
}
else if(s == "") //判斷髮送的數據是否爲空
{
MessageBox("請輸入信息");
}
else
{
update("我:"+ s+"\r\n");//消息上屏
send_edit->SetWindowText("");//清空輸入
send_edit->SetFocus(); //重獲焦點
}
}
//添加線程函數,接收數據
UINT recv_thd(LPVOID p)
{
int res;
char msg[1024];
CString s;
CcFileDlg * dlg = (CcFileDlg *) AfxGetApp()->GetMainWnd(); //得到應用程序活動主窗口的指針
dlg->update("開始聊天吧~\r\n");
while(1)
{
if( (res = recv(sock, msg, 1024, 0)) == -1) //接收數據,判斷是否接收成功
{
dlg->update("失去連接~\r\n");
dlg->btnconn->EnableWindow(TRUE);
break;
}
else
{
dlg->update("服務器:" + (CString(msg)).Mid(0,res)+ "\r\n");
}
}
return 0;
}
作者QQ:575674261
.NET學習交流羣 :324087998 (更多實戰項目源碼分享)
完整代碼打包: http://pan.baidu.com/s/1sj0KarN