网络聊天室实现报告Word下载.docx
《网络聊天室实现报告Word下载.docx》由会员分享,可在线阅读,更多相关《网络聊天室实现报告Word下载.docx(31页珍藏版)》请在冰点文库上搜索。
套接字基本概念:
套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。
可以将套接字看作不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。
套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。
套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。
各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。
套接字工作原理:
要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:
服务器监听,客户端请求,连接确认。
所谓服务器监听,是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。
而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
三、核心算法流程图
(1)在服务器上运行服务器端的应用程序,该程序一运行就开始服务器监听。
然后,在客户机上就可以打开客户端的应用程序。
程序打开后可以与服务器端应用程序进行连接,即进行客户端请求。
图2服务端与客户端互连流程图
(2)服务器运行原理:
当有客户端连接聊天室服务器后,服务器立刻为这个客户建立一个数据接收的线程(多用户程序必备)。
在接收线程中,如果收到聊天命令,就对其进行解析处理,服务器可以处理五种命令:
CONN\LIST\CHAT\PRIV\EXIT。
图3服务器运行流程图
(2)聊天室客户端的原理:
当客户端连接到服务器后,服务器立刻建立一个数据接收的独立线程。
在接收线程中,如果收到了聊天命令,就对其进行解析处理。
聊天室客户端一共处理的命令有五种:
OK\ERR\LIST\JOIN\QUIT命令。
图4客户端流程图
四、源代码
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
usingSystem.Net;
usingSystem.Threading;
usingSystem.Net.Sockets;
usingSystem.Diagnostics;
usingSystem.IO;
namespaceChatRoom
{
publicpartialclassChatRoom:
Form
{
Socket_mysocket;
stringmyName;
boolcheck;
ThreadthreadRecevie;
Dictionary<
string,ChatToOne>
DictionaryChatToOne=newDictionary<
();
publicChatRoom(Socketmysocket,string_myName)
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls=false;
_mysocket=mysocket;
myName=_myName;
check=true;
threadRecevie=newThread(newThreadStart(receive));
threadRecevie.Start();
stringsendChar="
}join{"
+_myName.Trim();
byte[]sendByte=Encoding.Unicode.GetBytes(sendChar.ToArray());
NetworkStreamnetstr=newNetworkStream(mysocket);
netstr.Write(sendByte,0,sendByte.Length);
netstr.Close();
}
protecteddelegatevoid_delegate(stringstr_redata);
//定义一个委托
protectedvoid_delegate1(stringstr_redata)
stringchatStr=str_redata.Replace("
{wrterChatToOne}"
"
"
);
string[]str=chatStr.Split(newstring[]{"
{SayWord}"
},StringSplitOptions.None);
stringstrMesg=str[1];
stringreceiveName=str[0];
if(DictionaryChatToOne.Keys.Contains(receiveName)){
if(DictionaryChatToOne[receiveName].IsDisposed)
ChatToOnefm=newChatToOne(receiveName);
fm.myscoket=_mysocket;
fm.myName=myName;
DictionaryChatToOne[receiveName]=fm;
fm.Show();
else
DictionaryChatToOne[receiveName].Activate();
DictionaryChatToOne[receiveName].WindowState=FormWindowState.Normal;
DictionaryChatToOne.Add(receiveName,fm);
stringnameDate=str[0]+"
"
+DateTime.Now.ToString("
yyyy/MM/ddHH:
mm:
ss"
((ChatToOne)DictionaryChatToOne[str[0]]).FchatStr(nameDate,strMesg);
return;
internalstaticHashtableclients=newHashtable();
//clients数组保存当前在线用户的client对象
privateTcpListenerlistener;
//该服务器默认的监听端口号
staticintMAX_NUM=100;
//服务器可以支持的客户端的最大连接数
internalstaticboolSocketServiceFlag=false;
//开始服务的标志
//获得本地局域网或者拨号动态分配的IP地址,在启动服务器时会用到IP地址
privatestringgetIPAddress()
//获得本机局域网IP地址
IPAddress[]Addresslist=Dns.GetHostEntry(Dns.GetHostName()).AddressList;
if(Addresslist.Length<
1)
return"
;
returnAddresslist[0].ToString();
//获得动态的IP地址
privatestaticstringgetDynamicIPAddress()
IPAddress[]Addresslist=Dns.GetHostEntry(Dns.GetHostName()).AddressList;
if(Addresslist.Length<
2)
returnAddresslist[1].ToString();
//服务器监听的端口号通过getValidPort()函数获得
privateintgetValidPort(stringport)
intlport;
//测试端口号是否有效
try
//是否为空
if(port=="
)
thrownewArgumentException("
端口号为空,不能启动服务器"
lport=System.Convert.ToInt32(port);
catch(Exceptione)
{Console.WriteLine("
无效的端口号:
+e.ToString());
this.rtbSocketMsg.AppendText("
+e.ToString()+"
\n"
return-1;
returnlport;
privatevoidbtnSocketStart_Click(objectsender,EventArgse)
intport=getValidPort(tbSocketPort.Text);
if(port<
0)
stringip=this.getIPAddress();
IPAddressipAdd=IPAddress.Parse(ip);
listener.Start();
//开始监听服务器端口
Socket服务器已经启动,正在监听"
+ip+"
端口号:
+this.tbSocketPort.Text+"
//启动一个新的线程,执行方法this.StartSocketListen,
//以便在一个独立的进程中执行确认与客户端Socket连接的操作
Form1.SocketServiceFlag=true;
Threadthread=newThread(newThreadStart(this.StartSocketListen));
thread.Start();
this.btnSocketStart.Enabled=false;
this.btnSocketStop.Enabled=true;
catch(Exceptionex)
this.rtbSocketMsg.AppendText(ex.Message.ToString()+"
//在新的线程中的操作,它主要用于当接收到一个客户端请求时,确认与客户端的链接
//并且立刻启动一个新的线程来处理和该客户端的信息交互
privatevoidStartSocketListen()
while(Form1.SocketServiceFlag)
{//当接收到一个客户端请求时,确认与客户端的链接
if(listener.Pending())//确认是否有挂起的连接请求
Socketsocket=listener.AcceptSocket();
//接收挂起的连接请求
if(clients.Count>
=MAX_NUM)
已经达到了最大连接数:
+MAX_NUM+"
拒绝新的链接\n"
socket.Close();
//启动一个新的线程
//执行方法this.ServiceClient,处理用户相应的请求
ChatSever.Client.Clientclient=newChatSever.Client.Client(this,socket);
ThreadclientService=newThread(newThreadStart(client.ServiceClient));
clientService.Start();
Thread.Sleep(200);
//提高性能整体速度
//client定义
publicclassClient
privatestringname;
//保存用户名
privateSocketcurrentSocket=null;
//保存与当前用户连接的Socket对象
privatestringipAddress;
//保存用户的IP地址
privateForm1server;
//保存当前连接状态
//Closed--connected--closed
privatestringstate="
closed"
publicClient(Form1server,SocketclientSocket)
this.server=server;
this.currentSocket=clientSocket;
ipAddress=getRemoteIPAddress();
publicstringName
get
returnname;
set
name=value;
publicSocketCurrentSocket
returncurrentSocket;
//ipAddress
privatestringgetRemoteIPAddress()
return((IPEndPoint)currentSocket.RemoteEndPoint).Address.ToString();
//SendToClient()方法实现了向客户端发送命令请求的功能
privatevoidSendToClient(Clientclient,stringmsg)
System.Byte[]message=System.Text.Encoding.Default.GetBytes(msg.ToCharArray());
client.currentSocket.Send(message,message.Length,0);
//ServiceClient方法用于和客户端进行数据通信,包括接收客户端的请求
//它根据不同的请求命令执行相应的操作,并将处理结果返回到客户端
//ServiceClient()函数为服务器接收客户数据的线程主体,主要用来接收用户发送来的数据,并处理聊天命令
publicvoidServiceClient()
string[]tokens=null;
byte[]buff=newbyte[1024];
boolkeepConnect=true;
//用循环来不断地与客户端进行交互,直到客户端发出“EXIT”命令
//将keepConnect职为false,退出循环,关闭连接,并中止当前线程
while(keepConnect&
&
Form1.SocketServiceFlag)
//tokens=null;
if(currentSocket==null||currentSocket.Available<
Thread.Sleep(300);
continue;
//接收数据并存入BUFF数组中
intlen=currentSocket.Receive(buff);
//将字符数组转化为字符串
stringclientCommand=System.Text.Encoding.Default.GetString(buff,0,len);
//tokens【0】中保存了命令标志符(CONNCHATPRIVLIST或EXIT)
tokens=clientCommand.Split(newchar[]{'
|'
});
if(tokens==null)
catch(Exceptione)
server.updateUI("
发送异常:
+e.ToString());
//以上代码主要用于服务器初始化和接收客户端发送来的数据。
它在对用户数据进行解析后,把用户命令转换为数组方式。
if(tokens[0]=="
CONN"
//此时接收到的命令格式化为命令标识符CONN|发送者的用户|tokens[1]中保存了发送者的用户名
this.name=tokens[1];
if(Form1.clients.Contains(this.name))
SendToClient(this,"
ERR|User"
+this.name+"
已经存在"
HashtablesyncClients=Hashtable.Synchronized(Form1.clients);
syncClients.Add(this.name,this);
//更新界面
server.addUser(this.name);
//对每一个当前在线的用户发//送JOIN消息命令和LIST消息命令,以此来跟新客户端的当前在线用户列表
System.Collections.IEnumeratormyEnumerator=Form1.clients.Values.GetEnumerator();
while(myEnumerator.MoveNext())
Clientc=(Client)myEnumerator.Current;
//将发送者的用户名:
发送内容转发给用户
SendToClient(c,message);
QUIT"
//退出当前线程break;
//客户端设计:
//包含一个类ChatClientForm,该类封装了聊天室客户端界面和聊天命令处理逻辑。
//其中一个重要的类TcpClient类(用于与服务器的连接)
TcpClienttcpClient;
//与服务器的链接
privateNetworkStreamStream;
//与服务器数据交互的流通道
privatestaticstringCLOSED="
privatestaticstringCONNECTED="
connected"
privatestringstate=CLOSED;
privateboolstopFlag