这是上个学期网络通讯的课程设计所做的作业,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