一个简单的多线程服务器客户端即时通讯应用程序






4.32/5 (25投票s)
一个使用TCP监听器和TCP客户端的简单多线程服务器客户端即时通讯应用程序。允许客户端之间以及与服务器进行个人消息和会议通讯。

库项目
我创建了一个ChatLibrary
,它将包含有效的命令,以及一个Message
,它将包含解析功能。
public enum Command
{
Login = 0,
PersonalMessage = 1,
ClientList = 2,
Conference = 3,
Logout = 4
};
public class Message
{
string strSender;
string strReceiver;
Command cmdMessageCommand;
string strMessageDetail;
public Message ()
{
}
public Message (byte [] rawMessage)
{
string strRawStringMessage = System.Text.Encoding.ASCII.GetString (rawMessage);
string [] strRawStringMessageArray = strRawStringMessage.Split(new char []{'|'});
this.strSender = strRawStringMessageArray[1];
this.strReceiver = strRawStringMessageArray[2];
this.cmdMessageCommand = (Command) Convert.ToInt32(strRawStringMessageArray[3]);
this.MessageDetail = strRawStringMessageArray[4];
}
...
public byte [] GetRawMessage ()
{
System.Text.StringBuilder sbMessage = new System.Text.StringBuilder ("John");
sbMessage.Append("|");
sbMessage.Append(strSender);
sbMessage.Append("|");
sbMessage.Append(strReceiver);
sbMessage.Append("|");
sbMessage.Append((int)cmdMessageCommand);
sbMessage.Append("|");
sbMessage.Append(strMessageDetail);
sbMessage.Append("|");
return System.Text.Encoding.ASCII.GetBytes(sbMessage.ToString());
}
...
服务器项目
我创建了一个SocketServer
,它调用TCPListener.Start()
方法
IPEndPoint endPoint = new IPEndPoint (ipaAddress, iPort);
listener = new TcpListener (endPoint);
listener.Start();
调用Listen
后,将创建一个新线程来监听传入的客户端
thrListenForClients = new Thread(new ThreadStart(ListenForClients));
thrListenForClients.Start();
ListenForClients
方法将等待连接并会将传入的套接字分配给一个新的Client
实例
Client acceptClient = new Client();
acceptClient.Socket = listener.AcceptTcpClient();
listenForMessageDelegate = new ListenForMessageDelegate (ListenForMessages);
listenForMessageDelegate.BeginInvoke
(acceptClient, new AsyncCallback(ListenForMessagesCallback), "Completed");
顺便说一下,Client
是一个包含TCPClient
的类,以便我们可以跟踪我们的连接
public class Client {
string strName;
TcpClient tcpClient;
public Client()
{
}
public string Name
{
get{return strName;}
set{ this.strName = value;}
}
public TcpClient Socket
{
get{return tcpClient;}
set{ this.tcpClient = value;}
}
public void SendMessage (Message sendMessage)
{
NetworkStream stream = tcpClient.GetStream();
stream.Write(sendMessage.GetRawMessage() , 0, sendMessage.GetRawMessage().Length);
}
}
我们的服务器现在已准备好监听传入的消息。为此,我们将接收到的客户端套接字传递过去,然后创建一个循环。我们使用NetworkStream
来读取消息
NetworkStream stream = client.Socket.GetStream();
byte [] bytAcceptMessage = new byte [1024];
stream.Read(bytAcceptMessage, 0, bytAcceptMessage.Length);
Message message = new ChatLibrary.Message(bytAcceptMessage);
收到消息后,我们可以做任何想做的事情
txtStatus.Text += "\r\n" + strDisplayMessageType +
strWriteText.TrimStart(new char[]{'\r','\n'});
我的SocketServer
利用了一些使编码更容易的事件
public event ClientConnectedEventHandler ClientConnected;
public event ClientDisconnectingEventHandler ClientDisconnecting;
public event MessageReceivedEventHandler MessageReceived;
在我的ServerForm
代码中,我所做的是保留一个连接的Client
的副本在ClientCollection
中,该集合继承自System.Collections.CollectionBase
。这样,我可以遍历Client
s。
客户端项目
Client
基本上做同样的事情。我创建了一个ClientSocket
,它将创建一个TCPListener
并调用Connect()
IPEndPoint serverEndpoint = new IPEndPoint (ipaAddress , iPort);
tcpClient = new TcpClient ();
tcpClient.Connect(serverEndpoint);
thrListenForMessages = new Thread (new ThreadStart(ListenForMessages));
thrListenForMessages.Start();
ListenForMessages
将要做的是使用NetworkStream.Read()
循环
stream = tcpClient.GetStream();
byte [] bytRawMessage = new byte [1024];
stream.Read(bytRawMessage, 0, bytRawMessage.Length);
ChatMessage receivedMessage = new ChatLibrary.Message (bytRawMessage);
然后我们执行相同的过程,使用接收到的字节创建一个Message
。
再次说明,我的目标是创建一个类似YM/MSN的即时通讯工具,所以我创建了两个UI表单。MessengerForm
和ClientWindow
。MessengerForm
是实例化ClientSocket
并接收消息的类。收到消息后,它调用MessengerWindow
,该窗口应显示文本。
请注意,我没有进行常规实例化。我调用了Invoke
以便能够使我的控件线程安全
this.Invoke(createNewClientDelegate, new object []{receivedMessage});
历史
- 2006年6月4日:初始版本