完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx

上传人:b****5 文档编号:19718366 上传时间:2023-01-09 格式:DOCX 页数:52 大小:623.05KB
下载 相关 举报
完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx_第1页
第1页 / 共52页
完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx_第2页
第2页 / 共52页
完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx_第3页
第3页 / 共52页
完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx_第4页
第4页 / 共52页
完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx_第5页
第5页 / 共52页
点击查看更多>>
下载资源
资源描述

完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx

《完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx》由会员分享,可在线阅读,更多相关《完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx(52页珍藏版)》请在冰豆网上搜索。

完整word版网络编程基于TCP的简易聊天室实验报告Word格式.docx

集成开发环境:

MicrosoftVisualStudio2010

二、流程图

三、系统简介

1.界面

本软件使用DOS控制台界面,界面风格较为朴素。

服务器:

客户端:

2.软件功能

本软件实现了聊天室基本的功能,包括公开聊天,私聊,获取在线用户,更改昵称,获得帮助等。

1)公开聊天

在光标处直接输入消息后按回车即为发送公开聊天,如下图所示。

2)私聊

使用命令【/m对方UID消息】即可发送私聊,私聊只有对方可以看到,如下图所示:

客户端1,密聊UID为132的用户。

发送后

客户端2,UID为132的用户收到私聊消息。

3)获取在线用户列表

使用命令【/list】即可获得在线用户列表,用户列表会议系统消息的方式返回,如下图所示。

命令

4)更改昵称

使用命令【/name你的新昵称】即可立即更改昵称,成功修改后服务器会以系统消息的方式返回成功修改的提示。

命令发送后

5)帮助信息

使用命令【/help】即可查看服务器的欢迎信息,里面包含了该聊天室的使用帮助,如下图所示。

3.系统设计

开发本软件时,我使用了面向对象的思想,把服务器和客户端封装成对应的类,类设计将会在下一节做详细介绍。

通行方面我在服务器接受客户端消息,和客户端接受服务器消息时使用了select模型,发送信息我使用的是普通的socket原语。

基本原理为服务器与客户端建立TCP连接,然后服务器负责路由消息到各个客户端。

4.优点与缺点

本软件对流程复杂的SELECT模型进行了细致的拆分与抽象,做到了逻辑流程清晰,每个函数简洁易懂,层次分明。

例如服务器启动函数:

voidChatServer:

:

Start()

{

InitListenSocket();

Bind();

Listen();

InitFDSocket();

while(true)

{

DoSelect();

}

}

try

charServer.Start();

catch(SockExceptione)

cout<

<

e.GetErrorInfo()<

endl;

"

[SystemError]ErrorCode:

"

<

e.GetErrorCode()<

它其实就完成了一个简单的流程,初始化socket,绑定,监听,初始化fd_socket集合,死循环调用select。

通过合理的封装底层原语和加入异常处理(异常交给顶层处理),使得代码专注于业务流程而不是繁杂的异常判断语句,在看下面这个函数DoSelect()。

DoSelect()

m_fdRead=m_fdSocket;

intnRet=Select();

if(nRet>

0)

for(inti=0;

i<

m_fdRead.fd_count;

i++)

DoFDRead(m_fdRead.fd_array[i]);

它也只完成一个简单的流程,调用select,然后循环处理有读事件的socket。

DoRead(SOCKETsRead)

if(sRead==m_sListen)

RecvNewConnect();

else

m_sNowClient=sRead;

接下来的DoFDRead()函数完成的事情也非常直接,如果有事件的socket是监听socket的话,那么就是接收到了一个新的连接,否则是接收到了新的小。

从上面这个简单的例子中可以看到,本软件最大的优点就是精心设计的类和函数。

避免了使用select模型常见的反复嵌套的循环和判断,每个函数清晰明了。

本系统还存在以下不足,首先是没有对界面做更深入的优化,只是做了最基本的调整,让输入输出更加雅观,其次是底层原语的封装并没有考虑到泛用性。

四、系统详细设计

这部分的文档在编码之前已经基本完成,由于时间较为仓促,部分内容可能和实际有所出入。

1.ChatServer类

该类负责完成服务器所有操作。

1)类图

2)成员变量

Map<

SOCKET,string>

m_clients聊天者的SOCKET与昵称的映射

fd_setm_fdSocket可用套接字集合

fd_setm_fdRead有事件发生的套接字集合

SOCKETm_sListen监听Socket

SOCKETm_sNowClient当前处理的客户套接字

intm_nPort监听端口

3)方法设计

voidBind()

voidListen()

voidSelect()

intRecv()

SOCKETAccept()

封装底层原语,并加入异常机制,使得外部调用简约明了。

构造函数

传入监听端口,初始化m_nPort

Start()

1)初始化监听套接字:

voidInitListenSocket()

2)绑定套接字至本地机器:

3)进入监听模式(设置为非阻塞):

4)初始化可用套接字集合voidInitFDSocket()

5)死循环,调用select方法DoSelect()

6)结束

DoSelect()

1)令m_fdRead=m_fdSocket

2)调用Select()

3)循环处理Select的结果DoFdRead(SocketsRead)

4)结束

DoFdRead(intiReadIndex)

1)判断是否为m_sListen

2)是m_sListenRecvNewConnect()

3)否则令m_sNowClient=m_fdRead[iReadIndex],调用RecvNewMessage()

RecvNewConnect()

1)判断是否达到套接字上线

2)调用Accept(),接收连接sClient

3)添加sCilent至m_fdSocket

4)添加套接字至m_clientsAddClientToInfoMap(stringname)

AddClientToInfoMap(stringname)

1)以SOKCET为键,name为值加入MAP

RecvNewMessage()

1)调用Recv函数

2)是否为命令IsCommand(stringstr)

3)是,则DoCommand(stringcmd)

4)否,则DoMessage(stringmsg)

5)结束

IsCommand(stringstr)

1)判断是否以"

/"

开头

DoCommand(stringcmd)

1)判断指令,并解析命令与参数(argc,argv)

2)调用指令处理函数

3)假设只有SetName命令,那么则将对应的套接字的名称设置

DoMessage(stringmsg)

1)拼接消息与名字BuildMsg(stringmsg)

2)在服务器上输出

3)消息路由DispatchMessage(stringmsg)

BuildMsg(stringmsg)

1)从m_clients中取出用户昵称

2)拼接字符串,形成格式如下

超人君(127.0.0.1)23:

49:

48说:

大家好!

即为:

昵称(IP地址)时间说:

消息正文

3)返回

DispatchMessage(stringmsg)

1)构造迭代器

2)遍历m_clients,若不是自身,则派送消息Send()

2.ChatClient类

该类负责处理客户端的所有操作。

2)字段设计

SOCKETm_sClient客户端自身的socket

SOCKETm_sServer服务器socket

stringm_name昵称

sockaddr_inm_ServerAddr;

服务器地址

根据端口号和服务器IP初始化m_server

Connect()

voidSend()

intSelect()

封装底层原语,加入异常处理,使得外部调用节约优雅

voidStart()

1)初始化套接字InitClientSocket()

2)连接服务器Connect()设置为非阻塞模式

3)获取名字并发送至服务器InitName()

4)创建新线程并显示替他用户发言线程函数RecvMsgThread()

5)循环SendMsg()

6)关闭客户端CloseClient()

InitName()

1)提示输入昵称

2)获取昵称

3)合法性判断判断重复

4)添加命令格式

5)发送至服务器

SendMsg()

1)读取一行消息

2)判断是否为命令IsCommand(stringstr)

3)命令:

处理命令DoCommand(stringcmd)

4)消息:

处理消息DoMessage(stringmsg)

1)发送消息Send()

2)本地回显

RecvMsgThread()

1)初始化fdSocket,将m_sClient加入

2)创建fdRead

3)死循环,将m_sClient拷贝至fdRead

4)调用Select

5)循环,并输出收到的消息Recv()

3.SocketException类

该类负责记录SOKCET错误的代码以及错误信息。

5.命令协议

命令格式为/命令参数1参数2

1.退出:

/exit

2.获取在线用户列表:

/getuser

3.私聊:

/mUID信息

4.清屏:

/clear

5.帮助:

/help

处理方式

IsCommand(stringstr)负责解析是否为命令

判断首字母是否为斜杠"

str.at(0)=='

/'

ResoveCommand(stringcmd,int&

argc,stringargv[])若是命令将命令解析为argc,argv

DoCommand(stringcmd)处理命令,调用具体的XXX命令处理函数DoCmdXXXX()。

6.消息格式

1)公共消息

超人君(127.0.0.1)UID:

100说:

大家好!

李四(127.0.0.1)UID:

101说:

你好!

你悄悄地对ABCUID:

100说:

你好

CDFUID:

101悄悄地对你说:

3)服务器消息

【系统消息】XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。

4)程序内部提示

[SystemInfo]xxxxxxxxxxxxxxxxxxxxxxxxx

五、系统测试

1.服务器使用错误

2.客户端使用错误

3.启动服务器

4.启动客户端

客户端出现欢迎信息以及昵称输入提示。

服务器出现连接提示

5.关闭客户端

服务器出现断开连接提示

6.启动服务器错误提示

给出错误提示信息和提示代码

7.公开聊天

所有客户端以及服务器都会显示。

8.私聊

只有私聊的二人才能看到聊天信息,其他用户和服务器无法看到。

9.错误的私聊

私聊自己会得到一个错误提示

私聊不存在的用户也会得到一个错误提示

10.更名

11.帮助

12.非法指令

非法指令会给出错误提示。

13.非法的指令参数

14.连接服务器失败

六、心得体会

这次实现我深入研究了select模型的使用,完成了一个简易的聊天室。

这次试验也使我在编程技巧方面也有了很大的提高。

七、完整代码

Charserverd.cpp服务器main函数文件

#include"

ChatServer.h"

SockException.h"

InitSock.h"

#include<

iostream>

usingnamespacestd;

InitSockinitSock;

intmain(intargc,char*argv[])

if(argc<

2)

cout<

Usage:

argv[0]<

Port"

return1;

ChatServercharServer(atoi(argv[1]));

try

charServer.Start();

catch(SockException&

e)

e.GetErrorInfo()<

[SystemError]ErrorCode:

e.GetErrorCode()<

ChatServer.h服务器类头文件

#ifndefCHAT_SERVER_H

#defineCHAT_SERVER_H

WinSock2.h>

string>

map>

ClientInfo.h"

classChatServer

public:

voidStart();

voidEnd();

ChatServer(intnPort);

~ChatServer(void);

private:

voidInitFDSocket();

voidDoSelect();

voidDoFDRead(SOCKETsRead);

voidRecvNewConnect();

stringIPAddrToString(sockaddr_insin);

voidAddClientToInfoMap(ClientInfoinfo);

voidRecvNewMessage();

boolIsCommand(stringstr);

voidDoCommand(stringcmd);

voidResoveCommand(stringcmd,int&

argc,stringargv[]);

voidDoCmdName(intargc,stringargv[]);

voidDoCmdGetUsers(intargc,stringargv[]);

voidDoMessage(stringmsg);

voidDoCmdPrivateMsg(intargc,stringargv[]);

stringBuildMessage(stringstr,boolbIsPublic);

stringBuildSystemMsg(stringstr);

voidDispatchMessage(stringmsg);

voidCloseConnect();

stringIntToString(intnNum);

//============简单封装底层原语=============

voidInitListenSocket();

voidBind();

voidListen();

intSelect();

intRecv(charmsgBuff[]);

voidSend(stringmsg,SOCKETclient);

SOCKETAccept(sockaddr_in&

sin);

//==========================================

map<

SOCKET,ClientInfo>

m_clients;

fd_setm_fdSocket;

fd_setm_fdRead;

SOCKETm_sListen;

SOCKETm_sNowClient;

intm_nPort;

};

#endifCHAT_SERVER_H

ChatServer.cpp服务器类

#pragmacomment(lib,"

ws2_32.lib"

#defineMAX_BUFF_SIZE500

typedefmap<

iteratormap_it;

ChatServer:

ChatServer(intnPort)

this->

m_nPort=nPort;

DoSelect();

if(nRet>

for(inti=0;

i<

{

DoFDRead(m_fdRead.fd_array[i]);

}

DoFDRead(SOCKETsRead)

RecvNewConnect();

else

m_sNowClient=sRead;

RecvNewMessage();

if(m_fdSocket.fd_count>

=FD_SETSIZE)

[SystemInfo]接受连接达到上限,拒绝连接"

return;

sockaddr_inclientAddr;

m_sNowClient=Accept(clientAddr);

ClientInfoclientInfo(clientAddr);

[SystemInfo]接受来自"

clientInfo.GetIp()<

的连接"

FD_SET(m_sNowClient,&

m_fdSocket);

AddClientToInfoMap(clientInfo);

stringChatServer:

IPAddrToString(sockaddr_insin)

stringstr=inet_ntoa(sin.sin_addr);

str.append("

);

charszFormat[20];

str.append(ltoa(ntohs(sin.sin_port),szFormat,10));

returnstr;

AddClientToInfoMap(ClientInfoinfo)

m_clients[m_sNowClient]=info;

charmsgBuff[MAX_BUFF_SIZE];

intnRet=Recv(msgBuff);

stringmsg(msgBuff);

if(nRet<

=0)return;

if(IsCommand(msg))

DoCommand(msg);

DoMessage(msg);

Do

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 企业管理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1