2024年2月20日发(作者:)

网络编程课程设计

——局域网聊天器

院系: 信息科学与技术学院

专业: 计算机科学与技术

小组成员:

指导教师:

2015.12.19

一、引言

在网络越来越发达的今天,人们对网络的依赖性也越来越大,由此而产生的聊天工具也越来越多,如MSN、QQ、ICQ等,然而这些聊天工具如果没有了Internet的支持的话人们将无法使用他们进行聊天交流,即使通信双方在同一个局域网中也一样。本次课程设计的内容就是一种局域网聊天软件,它无需Internet的支持,可以让用户们不用出内网就能自由聊天。

最早美国加州大学Berkeley分校在UNIX下为TCP/IP协议开发了一个API,这个API就是著名的Berkeley Socket接口(套接字)。在桌面操作系统进入Windows时代后,仍然继承了Socket方法。在TCP/IP网络通信环境下,Socket数据传输是特殊的I/O,它也相当于文件描述符,具有一个类似于打开文件的函数调用-socket()。可以这样理解:Socket实际上是一个通信端点,通过它,用户的Socket程序可以通过网络和其他的Socket应用程序通信。本论文讨论了Windows环境下局域网聊天程序的设计思路和设计方法。其中网络通信部分采用流行的TCP/IP协议。程序采用典型的C/S(服务器/客户端)构架。用socket编程设计网络通讯。界面设计部分采用Windows MFC框架。本文最终设计了一个简易版本的聊天软件,包括登陆、退出、消息收/发等功能。

二、原理介绍

用Socket做一个局域网聊天工具程序设计成为简单的服务端和客户端之间的通信,但通过一些方法可以将这两者进行统一起来,让服务端也成为客户端,

让客户端也成为服务端,使它们之间可以互相随时不间断的通信。考虑到实现最原始的服务端和客户端之间的通信所需要的步骤对于写这样的程序是很有帮助的。

作为服务端, 首先,创建又一个Socket并监听。然后启动线程接收数据。用一个链表保存所有连上的客户,并通知连接成功。这样,客户就有机会处理这一事件并作一些动作。最后,当客户断开时,向服务器发送一个事件,服务器就可以做一些收尾工作。其中最关键的部分是收发部分和数据处理部分。

要声明一个Socket A并绑定(Bind)某一个IP+这个IP指定的通信端口, 比如这个是127.0.0.1:9050, 然后开始监听(Listen), Listen可以监听来自多个IP传过来的连接请求, 具体可以同时连接几个客户端, Listen方法中可以设定一个参数。如果Listen到某一个客户端发来连接请求了, 这时定义一个新的Socket B专门负责与这个客户端的通信, Socket B = ()。这时可以获取这个客户端的IP和端口,IPEndPoint C = (IPEndPoint)EndPoint,

s和分别表示客户端C的IP地址和端口。这时通过()方法就可以给C发送消息了, e()可以接收客户端C发来的信息。

作为客户端, 也需要声明一个Socket D并绑定某一个IP+本机一个未被占用的端口, 定义IPEndPoint E表示要进行连接的服务端Socket, 要指明E的IP和端口, 这样才可以进行端口对端口之间的通信,接下来就可以尝试t(E),连接成功之后就可以发送和接收数据了,(), e.

发送消息时,数据都是以字节或字节数组为单位进行传输的, 比如我客户端D要发送"Hello World"则要这样写: (es("Hello

World")).接受消息时, 也是以字节或字节数组。

三、功能描述

本系统由设置模块、挂机和聊天模块、查看信息模块等部分组成。设置模块主要完成用户信息的提交和选择服务器端或者客户端,聊天模块主要包括群聊、私聊,刷新在线用户列表、显示其他用户发送的信息、发送信息给其他用户等。查看信息模块主要功能是查看对方昵称、个性签名等。

体系结构图:

开始

设置挂机锁密码

设置本机角色

服务器端

客户端

设置IP及端口号

输入服务器IP

进入聊天

(设置模块)

开始

关闭

发送内容

输入挂机密关闭聊天窗口

码进入聊天

结束

(聊天模块)

四、操作截图

进入的界面如下图,点击图片可以换头像:

选择本机角色:

选为服务器后:

选择复制信息到剪贴板:

选择客户端后,需要输入服务器IP:

开启聊天:

界面右上角有个一时钟。在小功能中,选择可以修改个人信息:

点击左上角图片选择可以查看对方信息。

是挂机而不是退出。想要回到聊天就得输入一开始的密码,以便你挂机的时候不会被别人偷看:

当你挂机之后系统会提示:

重新进入之后就是系统提示上线:

五、总结

我们这次的课程设计是基于MFC的网络TCP 长连接socket通信。程序采用典型的C/S(服务器/客户端)构架。用socket编程设计网络通讯。界面设计部分采用Windows MFC框架。在此设计过程中主要经过了审题、需求分析,然后形成设计思路,设计所要实现的功能,最后进行程序的编写与调试,这个过程是不

断出现问题并不断的进行修改,深深的感受到亲自动手实践的重要性。在实验的过程中发现学好基础理论知识的重要性,在设计过程中,不断遇到问题,通过老师和同学的帮助,解决了过程中出现的各种问题。

我们接触到了很多关于socket的编程,使我们学到了新知识,通过几个星期的努力,虽然从中也发现了我们很多不足,可能其中还有不少问题,但我觉得最重要的是我们也从中得到很多;不敢说百分百的完成也应该基本上完成了课题任务,成功地实现课题目标。通过本次网络编程课程设计,我更加充分的理解了课本上的知识,并能够加以扩展,从而应用于实践当中,这几天的课程设计令我受益匪浅,很多平时模棱两可的知识点都认真复习并实践了。

附录(部分源代码):

// : 实现文件

//

#include "stdafx.h"

#include "Tiger.h"

#include "IPDialog.h"

#include "afxdialogex.h"

// IPDialog 对话框

extern int return_value;

int iscopy;

extern int root_app;

CString szIPaddress;

IMPLEMENT_DYNAMIC(IPDialog, CDialogEx)

IPDialog::IPDialog(CWnd* pParent /*=NULL*/)

: CDialogEx(IPDialog::IDD, pParent)

{

}

IPDialog::~IPDialog()

{

}

void IPDialog::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(IPDialog, CDialogEx)

ON_WM_CREATE()

ON_WM_TIMER()

ON_BN_CLICKED(IDOK, &IPDialog::OnBnClickedOk)

ON_BN_CLICKED(IDC_refresh, &IPDialog::OnBnClickedrefresh)

ON_WM_CLOSE()

END_MESSAGE_MAP()

// IPDialog 消息处理程序

BOOL IPDialog::OnInitDialog()

{

CDialogEx::OnInitDialog();

iscopy =0;

char HostName[100];

hostent * hn;

}

gethostname(HostName, sizeof(HostName));// 获得本机主机名

hn = gethostbyname(HostName);//根据本机主机名得到本机ip

szIPaddress = inet_ntoa(*(struct in_addr *)hn->h_addr_list[0]);//把ip换成字符串形式

if(szIPaddress != _T("127.0.0.1"))

{

SetDlgItemTextW(IDC_rootip,szIPaddress);

return_value = 0;

}

else

{

SetDlgItemTextW(IDC_rootip,_T("当前未连接到网络"));

return_value = -1;

MessageBox(_T("当前未连接到网络,请检查网络设置"),_T("错误"));

}

SetDlgItemInt(IDC_rootport,8989,1);

return TRUE; // return TRUE unless you set the focus to a control

// 异常: OCX 属性页应返回 FALSE

int IPDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CDialogEx::OnCreate(lpCreateStruct) == -1)

return -1;

// TODO: 在此添加您专用的创建代码

return 0;

}

void IPDialog::OnTimer(UINT_PTR nIDEvent)

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

CDialogEx::OnTimer(nIDEvent);

}

void IPDialog::OnBnClickedOk()

{

// TODO: 在此添加控件通知处理程序代码

if(szIPaddress != _T("127.0.0.1"))

{

if (this->OpenClipboard()) //如果能打开剪贴板

{

::EmptyClipboard(); //清空剪贴板,使该窗口成为剪贴板的拥有者

HGLOBAL hClip;

hClip = ::GlobalAlloc(GMEM_MOVEABLE, (gth() * 2) + 2); //判断要是文本数据,分配内存时多分配一个字符

TCHAR *pBuf;

pBuf = (TCHAR *)::GlobalLock(hClip);//锁定剪贴板

lstrcpy(pBuf, szIPaddress);//把CString转换

::GlobalUnlock(hClip);//解除锁定剪贴板

::SetClipboardData(CF_UNICODETEXT, hClip);//把文本数据发送到剪贴板

CF_UNICODETEXT为Unicode编码

::CloseClipboard();//关闭剪贴板

MessageBox(_T("服务器IP已复制到剪贴板,请通过其他渠道给予客户端用户"),_T("提示"));

}

iscopy = 1;

CDialogEx::OnOK();

}

else

{

MessageBox(_T("当前未连接到网络,请检查网络设置后点击刷新信息"),_T("错误"));

}

}

void IPDialog::OnBnClickedrefresh()

{

// TODO: 在此添加控件通知处理程序代码

char HostName[100];

hostent * hn;

gethostname(HostName, sizeof(HostName));// 获得本机主机名

hn = gethostbyname(HostName);//根据本机主机名得到本机ip

szIPaddress = inet_ntoa(*(struct in_addr *)hn->h_addr_list[0]);//把ip换成字符串形式

if(szIPaddress != _T("127.0.0.1"))

{

SetDlgItemTextW(IDC_rootip,szIPaddress);

return_value = 0;

}

else

{

SetDlgItemTextW(IDC_rootip,_T("当前未连接到网络"));

return_value = -1;

MessageBox(_T("当前未连接到网络,请检查网络设置"),_T("错误"));

}

SetDlgItemInt(IDC_rootport,8989,1);

}

void IPDialog::OnClose()

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

if(iscopy == 1)

{

root_app = 1;

CDialogEx::OnClose();

}

else

{

if(return_value == -1)

{

CDialogEx::OnClose();

}

else

{

if(MessageBox(_T("您还未复制IP,是否现在复制到剪贴板"),_T("提示"),MB_OKCANCEL) == IDOK)

{

if(szIPaddress != _T("127.0.0.1"))

{

if (this->OpenClipboard()) //如果能打开剪贴板

{

::EmptyClipboard(); //清空剪贴板,使该窗口成为剪贴板的拥有HGLOBAL hClip;

hClip = ::GlobalAlloc(GMEM_MOVEABLE, (gth()

* 2) + 2); //判断要是文本数据,分配内存时多分配一个字符

TCHAR *pBuf;

pBuf = (TCHAR *)::GlobalLock(hClip);//锁定剪贴板

lstrcpy(pBuf, szIPaddress);//把CString转换

::GlobalUnlock(hClip);//解除锁定剪贴板

::SetClipboardData(CF_UNICODETEXT, hClip);//把文本数据发送到剪贴板 CF_UNICODETEXT为Unicode编码

::CloseClipboard();//关闭剪贴板

MessageBox(_T("服务器IP已复制到剪贴板,请通过其他渠道给予客户端用户"),_T("提示"));

}

iscopy = 1;

root_app = 1;

CDialogEx::OnOK();

}

else

{

MessageBox(_T("当前未连接到网络,请检查网络设置后点击刷新信息"),_T("错误"));

}

}

}

}

}

// : 实现文件

//

#include "stdafx.h"

#include "Tiger.h"

#include "SelectserviceDialog.h"

#include "afxdialogex.h"

#include "IPDialog.h"

#include "SetrootipDialog.h"

// SelectserviceDialog 对话框

extern int return_value;

extern int root_app;

IMPLEMENT_DYNAMIC(SelectserviceDialog, CDialogEx)

SelectserviceDialog::SelectserviceDialog(CWnd* pParent /*=NULL*/)

: CDialogEx(SelectserviceDialog::IDD, pParent)

{

}

SelectserviceDialog::~SelectserviceDialog()

{

}

void SelectserviceDialog::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(SelectserviceDialog, CDialogEx)

ON_BN_CLICKED(IDC_Setasroot, &SelectserviceDialog::OnBnClickedSetasroot)

ON_WM_CLOSE()

ON_BN_CLICKED(IDC_Setasapp, &SelectserviceDialog::OnBnClickedSetasapp)

END_MESSAGE_MAP()

// SelectserviceDialog 消息处理程序

void SelectserviceDialog::OnBnClickedSetasroot()

{

// TODO: 在此添加控件通知处理程序代码

IPDialog dlg;

ShowWindow(SW_HIDE);

l();

root_app = 1;

CDialogEx::OnOK();

}

void SelectserviceDialog::OnClose()

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

if(root_app != 0)

CDialogEx::OnClose();

else

{

MessageBox(_T("请设定本机角色,点击设置服务器或客户端"),_T("警告"));

}

}

BOOL SelectserviceDialog::OnInitDialog()

{

CDialogEx::OnInitDialog();

// TODO: 在此添加额外的初始化

root_app =0;

return TRUE; // return TRUE unless you set the focus to a control

// 异常: OCX 属性页应返回 FALSE

}

void SelectserviceDialog::OnBnClickedSetasapp()

{

// TODO: 在此添加控件通知处理程序代码

SetrootipDialog dlg;

ShowWindow(SW_HIDE);

l();

root_app = 2;

CDialogEx::OnOK();

}

// : 实现文件

//

#include "stdafx.h"

#include "Tiger.h"

#include "PassDialog.h"

#include "afxdialogex.h"

// PassDialog 对话框

extern int isBack;

extern int local_headpicture;

extern CString gl_password;

int error_count;

IMPLEMENT_DYNAMIC(PassDialog, CDialogEx)

PassDialog::PassDialog(CWnd* pParent /*=NULL*/)

: CDialogEx(PassDialog::IDD, pParent)

{

}

PassDialog::~PassDialog()

{

}

void PassDialog::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

DDX_Control(pDX, IDC_mpicture, mpicture);

}

BEGIN_MESSAGE_MAP(PassDialog, CDialogEx)

ON_BN_CLICKED(IDC_EXIT, &PassDialog::OnBnClickedExit)

ON_BN_CLICKED(IDC_backin, &PassDialog::OnBnClickedbackin)

END_MESSAGE_MAP()

// PassDialog 消息处理程序

void PassDialog::OnBnClickedExit()

{

// TODO: 在此添加控件通知处理程序代码

isBack = 1;

CDialogEx::OnOK();

}

BOOL PassDialog::OnInitDialog()

{

CDialogEx::OnInitDialog();

// TODO: 在此添加额外的初始化

HBITMAP hBitmap;

hBitmap = ::LoadBitmap(AfxGetInstanceHandle(),MAKEINTRESOURCE(local_headpicture));

map(hBitmap);

isBack = 0;

error_count =0;

return TRUE; // return TRUE unless you set the focus to a control

// 异常: OCX 属性页应返回 FALSE

}

void PassDialog::OnBnClickedbackin()

{

// TODO: 在此添加控件通知处理程序代码

CString mypass;

GetDlgItemTextW(IDC_mpass,mypass);

if(mypass == gl_password)

{

isBack = 0;

CDialogEx::OnOK();

}

else

{

error_count ++;

if(error_count <3)

MessageBox(_T("密码错误,输错三次将自动关闭聊天,所有聊天记录不再保存!"),_T("错误"));

}

if(error_count == 3)

{

MessageBox(_T("密码错误,即将自动关闭!"),_T("错误"));

isBack = 2;

CDialogEx::OnOK();

}

}

// : 实现文件

//

#include "stdafx.h"

#include "Tiger.h"

#include "SelectserviceDialog.h"

#include "afxdialogex.h"

#include "IPDialog.h"

#include "SetrootipDialog.h"

// SelectserviceDialog 对话框

extern int return_value;

extern int root_app;

IMPLEMENT_DYNAMIC(SelectserviceDialog, CDialogEx)

SelectserviceDialog::SelectserviceDialog(CWnd* pParent /*=NULL*/)

: CDialogEx(SelectserviceDialog::IDD, pParent)

{

}

SelectserviceDialog::~SelectserviceDialog()

{

}

void SelectserviceDialog::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(SelectserviceDialog, CDialogEx)

ON_BN_CLICKED(IDC_Setasroot, &SelectserviceDialog::OnBnClickedSetasroot)

ON_WM_CLOSE()

ON_BN_CLICKED(IDC_Setasapp, &SelectserviceDialog::OnBnClickedSetasapp)

END_MESSAGE_MAP()

// SelectserviceDialog 消息处理程序

void SelectserviceDialog::OnBnClickedSetasroot()

{

// TODO: 在此添加控件通知处理程序代码

IPDialog dlg;

ShowWindow(SW_HIDE);

l();

root_app = 1;

CDialogEx::OnOK();

}

void SelectserviceDialog::OnClose()

{

// TODO: 在此添加消息处理程序代码和/或调用默认值

if(root_app != 0)

CDialogEx::OnClose();

else

{

MessageBox(_T("请设定本机角色,点击设置服务器或客户端"),_T("警告"));

}

}

BOOL SelectserviceDialog::OnInitDialog()

{

CDialogEx::OnInitDialog();

// TODO: 在此添加额外的初始化

root_app =0;

return TRUE; // return TRUE unless you set the focus to a control

// 异常: OCX 属性页应返回 FALSE

}

void SelectserviceDialog::OnBnClickedSetasapp()

{

// TODO: 在此添加控件通知处理程序代码

SetrootipDialog dlg;

ShowWindow(SW_HIDE);

l();

root_app = 2;

CDialogEx::OnOK();

}