C# 聊天软件.docx
《C# 聊天软件.docx》由会员分享,可在线阅读,更多相关《C# 聊天软件.docx(33页珍藏版)》请在冰点文库上搜索。
C#聊天软件
C#专业实践
专业:
软件工程
班级:
0801
学号:
200812052
姓名:
丁志均,邢皓,魏子龙
成绩:
二〇一一年九月
目录
1需求分析-3-
1.1开发背景-3-
1.2功能需求-3-
1.3软件结构-3-
1.3.1服务器端的设计-3-
1.3.2客户端设计-4-
2相关知识-5-
2.1TCP与Socket-5-
3详细设计-7-
3.1服务器端的实现-7-
3.2客户端的实现-18-
4设计总结-25-
1需求分析
1.1开发背景
为了满足学习研究需要,设计本在线C#聊天工具,它提供了基本的聊天功能。
1.2功能需求
①能查看所有在线用户
②能对所有在线用户进行群聊
③支持点对点的单聊
1.3软件结构
本程序包括两个大的方面,服务器端和客户端。
1.3.1服务器端的设计
服务器端管理着聊天任务,维持着一张当前在线用户的列表,转发用户送来的信息。
1)监听本机IP地址的一个指定的端口
2)根据客户端发送来的各种不同的请求,执行相应的操作,并将处理结果返回给该客户端。
3)服务器能够识别6种请求命令:
1CONNECT—连接聊天室
2JOIN—通知本人已加入聊天室
3LIST—抢客户端发送全部登陆用户名
4CHAT—群聊功能
5PRIV—私聊功能
6EXIT|QUIT—退出聊天室
1-1服务器端界面
1.3.2客户端设计
客户端应用程序包括用户登录窗口和用户聊天的主窗口,它允许用户登录到服务器,可以向服务器发送信息,同时可以接收从服务器返回的信息
1)向远程服务器发送连接请求
2)得到服务器程序的确认后,建立与服务器的连接,并获得与服务器交互的流通道
3)通过网络流通道与服务器端的程序进行数据通信。
向服务器发送服务器能够识别的四种指令请求,同时也接收服务器发回的命令。
客户端能够识别的命令有通知当前在线用户有新的用户进入聊天室,更新当前在线用户和关闭客户端程序。
1-2客户端界面
2相关知识
2.1TCP与Socket
1主要包括服务器端的实现和客户端的实现
采用TCP协议通信和Socket编程基础
TCP协议是面向连接的协议,它的实现需要数据发送方和数据接收方建立数据通信连接
具体流程如下:
4)服务器端首先创建服务器套接字
5)服务器套接字监听一个端口,等待客户端的请求
6)客户端创建一个客户端套接字
7)客户端向服务器发送连接请求
8)服务器确认与客户端的连接
9)客户端和服务器利用建立的连接进行通信
10)通信完毕后,客户端和服务器关闭各自的连接
2.Socket编程基础
利用Socket建立一个TCP服务器程序
1)创建一个服务器套接字,用IP地址和端口初始化服务器
2)监听服务器端口
3)确认与客户端的连接
4)处理客户端的请求并回应客户端
5)关闭服务器,释放服务器连接
利用Socket建立客户端程序
1)创建客户端套接字
2)连接服务器
3)得到与服务器通信的流通道
4)向服务器发送数据
5)接收从服务器发回的数据
6)断开连接
2-1服务器与客户端的通信流程
3详细设计
3.1服务器端的实现
为了使用Socke对象和Thread对象,在代码中加入名字空间Systen.Net、System.Net.Sockets和System.Thread的引用
在服务器端使用了多线程,每个用户通过处理一个单独的线程进行连接,当聊天服务器开始运行时,它就启动一个线程等待客户连接。
当接收到一个请求时,服务器立刻启动一个线程来处理和该客户端的信息交互。
这里自定义一个Client类,它用于保存每个当前在线用户的用户名和服务器连接的Socket对象。
当Socket连接一旦建立,就马上将其保存在一个Client对象中,以便让每个用户有自己的Socket,以后可以对不同用户的Socket对象进行操作,实现与客户端的数据交互。
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Collections;
usingSystem.Text;
usingSystem.Windows.Forms;
usingSystem.Net;
usingSystem.Net.Sockets;//引入Socket的命名空间
usingSystem.Threading;//引入线程的命名空间
namespaceChatApplicationServer
{
publicpartialclassfrmServerMain:
Form
{
//clients数组保存当前在线用户的client对象
internalstaticHashtableclients=newHashtable();
//该服务器默认的监听器
privateTcpListenerlistener;
//默认最大支持的客户端连接数
staticintMAXUSER=100;
//服务器是否开启的标志
internalstaticboolServerFlag=false;
publicfrmServerMain()
{
InitializeComponent();
}
publicdelegatevoidAppendMsgEventHandler(RichTextBoxrb,stringmsg);//定义在线程中操作不同线程创建的控件的委托
publicdelegatevoidAppendUserEventHandler(ListBoxlb,stringusername);
//开启服务器
privatevoidbtStartServer_Click(objectsender,EventArgse)
{
intiPort=this.returnValidPort(tbServerPort.Text.Trim());
if(iPort<0)
{
MessageBox.Show("错误的端口信息!
","错误提示");
return;
}
stringip=this.returnIpAddress();
try
{
IPAddressuserIP=IPAddress.Parse(ip);
//创建服务器套接字
listener=newTcpListener(userIP,iPort);
listener.Start();
this.rbChatContent.AppendText("服务器已经启动,正在监听"+ip+"端口号:
"+tbServerPort.Text+"..........\n");
frmServerMain.ServerFlag=true;
//以下方法启动一个新的线程,执行监听方法
//以便在一个独立的进程中执行确认与客户端连接的操作
Threadthread=newThread(StartListen);
thread.Start();
btStartServer.Enabled=false;
btStopServer.Enabled=true;
}
catch(Exceptionex)
{
this.rbChatContent.AppendText(ex.Message+"\n");
}
}
//用于接收客户端的请求,确认与客户端的连接
//并且启动一个新的线程处理客户端的请求
privatevoidStartListen()
{
while(frmServerMain.ServerFlag)
{
try
{
//当接收到客户端请求时,确认与客户端的连接
if(listener.Pending())
{
SocketnewSocket=listener.AcceptSocket();
if(clients.Count>=MAXUSER)
{
MessageBox.Show("连接数已经超过允许连接的最大数"+MAXUSER.ToString()+",拒绝新的连接!
","错误提示");
this.rbChatContent.AppendText("连接数已经超过允许连接的最大数"+MAXUSER.ToString()+",拒绝新的连接!
");
newSocket.Close();
}
else
{
//启动一个新的线程,处理用户相应的请求
ChatClientnewClient=newChatClient(this,newSocket);
ThreadClientThread=newThread(newClient.ClientService);
ClientThread.Start();
}
}
Thread.Sleep(50);
}
catch(Exceptionex)
{
this.UpdateMsg(ex.Message);
}
}
}
//获取有效的端口号
privateintreturnValidPort(stringstrPort)
{
intport;
//测试端口号是否有效
try
{
if(tbServerPort.Text=="")
{
thrownewArgumentException("端口号为空,不能启动服务器!
");
}
else
{
port=Convert.ToInt32(tbServerPort.Text.Trim());
}
}
catch(Exceptionex)
{
this.rbChatContent.AppendText("无效的端口号:
"+ex.Message+"\n");
return-1;
}
returnport;
}
///
///获取本机局域网IP地址
///
///
privatestringreturnIpAddress()
{
IPAddress[]AddressList=Dns.GetHostByName(Dns.GetHostName()).AddressList;
if(AddressList.Length<1)
{
return"";
}
returnAddressList[0].ToString();
}
///
///获得拨号动态分配IP地址
///
///
privatestaticstringgetDynamicIPAddress()
{
IPAddress[]AddressList=Dns.GetHostByName(Dns.GetHostName()).AddressList;
if(AddressList.Length<2)
{
return"";
}
returnAddressList[1].ToString();
}
///
///更新显示信息
///
///
publicvoidUpdateMsg(stringmsg)
{
Invoke(newAppendMsgEventHandler(AppendMsgEvent),rbChatContent,msg+"\n");
}
///
///添加用户更新界面
///
///
publicvoidAddUser(stringname)
{
stringstr="【"+name+"】"+"已经加入聊天!
\n";
Invoke(newAppendMsgEventHandler(AppendMsgEvent),rbChatContent,str);//AppendMsgEvent(rbChatContent,"【"+name+"】"+"已经加入聊天!
\n");
//将刚加入的用户添加进用户列表
Invoke(newAppendUserEventHandler(AppendUserEvent),lbOnlineUser,name);
this.tslOnlineUserNum.Text=Convert.ToString(clients.Count);
}
publicvoidAppendMsgEvent(RichTextBoxrb,stringmsg)
{
rb.AppendText(msg);
}
publicvoidAppendUserEvent(ListBoxlb,stringusername)
{
lb.Items.Add(username);
}
///
///获取用户列表
///
///
publicstringGetUserList()
{
stringuserList="";
for(inti=0;i{
userList=userList+lbOnlineUser.Items[i].ToString()+"|";
}
returnuserList;
}
///
///移出用户
///
///
publicvoidRemoveUser(stringname)
{
this.rbChatContent.AppendText(name+"已经离开聊天室\n");
//将刚连接的用户名加入到当前在线用户列表中
this.lbOnlineUser.Items.Remove(name);
this.tslOnlineUserNum.Text=System.Convert.ToString(clients.Count);
}
//停止服务器
privatevoidbtStopServer_Click(objectsender,EventArgse)
{
frmServerMain.ServerFlag=false;
btStartServer.Enabled=true;
btStopServer.Enabled=false;
UpdateMsg("服务器已经停止监听.\n");
}
//关闭窗体
privatevoidfrmServerMain_FormClosing(objectsender,FormClosingEventArgse)
{
frmServerMain.ServerFlag=false;
}
privatevoidtbServerPort_TextChanged(objectsender,EventArgse)
{
this.btStartServer.Enabled=(this.tbServerPort.Text!
="");
}
privatevoidfrmServerMain_Load(objectsender,EventArgse)
{
}
}
publicclassChatClient
{
privatestringname;
privateSocketcurrentSocket=null;
privatestringipAddress;
privatefrmServerMainserver;
//保留当前连接的状态
//CLOSED-->CONNECTED-->CLOSED
stringconnState="closed";
publicChatClient(frmServerMainserver,SocketcurrentSocket)
{
this.server=server;
this.currentSocket=currentSocket;
}
publicstringName
{
get{returnname;}
set{name=value;}
}
publicSocketCurrentSocket
{
get{returncurrentSocket;}
set{currentSocket=value;}
}
publicstringIpAddress
{
get{returnipAddress;}
}
//获取远程IP地址
privatestringgetRemoteIp()
{
return((IPEndPoint)currentSocket.RemoteEndPoint).Address.ToString();
}
//和客户端进行数据通信,包括接收客户端的请求
//根据不同的请求命令,执行相应的操作,并将操作结果返回给客户端
publicvoidClientService()
{
string[]acceptStr=null;//保存消息字符
byte[]buff=newbyte[1024];//缓冲区
boolkeepConnected=true;
//用循环不断地与客户端进行交互,直到其发出EXIT或者QUIT命令
//将keepConnected设置为false,退出循环,关闭当前连接,中止当前线程
while(keepConnected&&frmServerMain.ServerFlag)
{
acceptStr=null;
try
{
if(currentSocket==null||currentSocket.Available<1)
{
Thread.Sleep(300);
continue;
}
//接收信息存入buff数组中
intlength=currentSocket.Receive(buff);
//将字符数组转换为字符串
stringCommand=Encoding.Default.GetString(buff,0,length);
//对字符串按分隔符拆分
acceptStr=Command.Split(newchar[]{'|'});
if(acceptStr==null)
{
Thread.Sleep(200);
continue;
}
}
catch(Exceptionex)
{
server.UpdateMsg("程序发生异常,异常信息:
"+ex.Message);
}
if(acceptStr[0]=="CONNECT")
{
//如果为CONECT,则它的命令格式为“CONNECT|发送者的用户名:
附件信息”
//则acceptStr[1]保存的就是用户名
this.name=acceptStr[1];
if(frmServerMain.clients.Contains(this.name))
{
SendToClient(this,"ERORR|User"+this.name+"已经存在!
");
}
else
{
HashtablesynClients=Hashtable.Synchronized(frmServerMain.clients);
synClients.Add(this.name,this);
//更新界面
server.AddUser(this.name);
//对每一个在线用户发送JOIN和LIST消息命令更新用户列表
IEnumeratormyIEnumerator=frmServerMain.clients.Values.GetEnumerator();
while(myIEnumerator.MoveNext())
{
ChatClientcClient=(ChatClient)myIEnumerator.Current;
SendToClient(cClient,"JOIN|"+this.name+"|");
Thread.Sleep(100);
}
//更新连接状态
connState="connected";
SendToClient(this,"OK");
//向客户端发送LIST命令更新用户列表
stringmsgUsers="LIST|"+server.GetUserList();
SendToClient(this,msgUsers);
}
}
elseif(acceptStr[0]=="LIST")
{
if(connState=="CONNECTED")
{
//向客户端发送LIST命令,以此更新客户端的当前在线用户列表
stringmsgUsers="LIST|"+server.GetUserList();
SendToClient(this,msgUsers);
}
else
{
SendToClient(this,"ERROR|服务器未连接,请重新登录");
}
}
elseif(acceptStr[0]=="CHAT")
{
if(connState=="connected")
{
//此时接收到的命令的格式为:
//命令标志符(CHAT)|发送者的用户名:
发送内容|
//向所有当前在线的用户转发此信息
IEnumeratormyEnumerator=frmServerMain.