本案为C/S聊天程序。通过应用Socket可在服务器和客户端之间建立通道,完成聊天功能,并且支持私聊,虽然比现实中的聊天室要小,但可使读者进一步掌握Socket的用法,为实现复杂网络程序打好基础。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
关键字:C/S, 聊天,Socket
一、引言
随着Internet发展,网络即时通信工具使用率越来越高,先后出现了很多网络聊天室,聊天工具。他们基于Socket通信方式。因此,Socket是网络编程的基础,任何需要进行网络通信的应用程序都需要Socket的支持。C#提供了一个System.Net包,里面包含许多处理网络通信的类库,用户可参考示例编写各种网络工具。
在计算机的世界里,凡是提供服务的一方我们称为服务器端(Server),而接受服务的另一方我们称作客户端(Client)。我们最常接触到例子是局域网络里的打印服务器所提供的打印服务:提供打印服务的计算机,我们可以说它是打印服务器;而使用打印服务器提供打印服务的另一方,我们则称作客户端。但是谁是客户端谁是服务器端也不是绝对的,例如倘若原提供服务之服务器端要使用其它机器所提供之服务,则所扮演之角色即转变为客户端。
而这种关系在因特网上,就变成使用者和网站的关系了。使用者透过调制解调器等设备上网,在浏览器中输入网址,透过HTTP 通讯协议向网站提出浏览网页的要求(Request)。网站收到使用者的要求后,将使用者要浏览的网页数据传输给使用者,这个动作称为响应(Response)。网站提供网页数据的服务,使用者接受网站所提供的数据服务;所以使用者在这里就是客户端,响应使用者要求的网站即称为服务器端。
不过客户端及服务器端的关系不见得一定建立在两台分开的机器上,同一台机器中也有这种主从关系的存在。提供服务的服务器端及接受服务的客户端也有可能都在同一台机器上,例如我们在提供网页的服务器上执行浏览器浏览本机所提供的网页,这样在同一台机器上就同时扮演服务器端及客户端。
二、工作原理及相关技术
程序基于WinForm的应用程序,支持多用户聊天,用户与用户之间私聊。
Socket:最早它是使用标准UNIX文件描述符(file descriptor)和其他程序通讯的方式。
Internet套接字:有两种类型:一种是“Stream Sockets”(流格式),另外一种是“Datagram Sockets”(数据包格式)。也会用到“SOCK_STREAM”和“SOCK_DGRAM”。数据报套接字有时也称为“无连接套接字”,如果确实要连接的时候可以用connect()。流式套接字是可靠的双向通讯的数据流。
客户—服务器(C/S):在网络上的所有东西都是在处理客户进程和服务器进程的交谈。当用telnet(客户)通过23号端口登录到主机,主机上运行的一个程序(一般叫telnetd,服务器)被激活,它处理这个连接,显示登录界面等。客户—服务器之间可以使用SOCK_STREAM、SOCK_DGRAM或者其他(只要它们采用相同的)。一些很好的客户—服务器的例子有telnet/telnetd、FTP/FTPd和bootp/bootpd。
request
<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
Client |
Server
|
response
TCPClient类:在网络中,支持HTTP、TCP和UDP的类组成了TCP/IP三层模型(请求响应层、应用协议层和传输层)的中间层—应用协议层,该层的类比位于最底层的Socket类提供了更高层次的抽象,它们封装TCP和UDP套接字的创建,不需要处理连接的细节。
Microsoft Visual C# 2008:(读作 C sharp)是一种编程语言,它是为生成在 .NET Framework上运行的多种应用程序而设计的。C# 简单、功能强大、类型安全,而且是面向对象的。C# 凭借它的许多创新,在保持 C 样式语言的表示形式和优美的同时,实现了应用程序的快速开发。
三、设计思路及实现的方法
1、设计思路
要通过互联网进行通信,至少要有一对套接字,其中一个运行客户机端,称为ClientSocket,另一个运行服务器端,称为ServiceSocket。
根据连接启动方式以及本地套接句要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听、客户端请求、连接确认。
服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
客户端请求:由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
连接确认:当服务器端套接字监听到或者接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
2、实现的方法
建立C# windows应用程序项,命名为ChatClient。在主窗体Form1中添加4个Button组件,一个ListBox组件,一个Textbox组件,一个RichTextbox组件,一个Checkbox组件。
四、核心代码及说明
1、新建客户端套接字:
TCPClient tcpclnt = new TCPClient( );
2、连接服务器(就是Service创建的服务器):
tcpclnt.Connect(“192.168.0.1”,8001);
3、读入字符串:
String str = Console.ReadLine( );
4、发送字符串:
ASCIIEncoding asen = new ASCIIEncoding ( );
Byte() ba = asen.GetBytes(str);
Console.WriteLine(“传输中……”);
Stm.Write(ba,0,ba,Lenfth);
5、部分源程序代码与解释:
服务器端连接初始化:
static void Main(String[] args)
{
Form1 cf = new Form1();
if(args.Length == 0)
cf.serveraddress = "192.168.0.1"; //服务器IP
else
cf.serveraddress = args[0];
Application.Run(cf);
}
“连接”按钮:
private void btnConnect_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
if(ChatOut.Text == "")
{
MessageBox.Show("连接前请在消息发送框中写上你的大名!","错误",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
else
clientname = ChatOut.Text;
EstablishConnection();
if(connected)
{
RegisterWithServer();
receive = new Thread(new ThreadStart(ReceiveChat));
receive.Start();
btnSend.Enabled = true;
btnConnect.Enabled = false;
ChatOut.Text = "";
}
}
“断开连接”按钮:
private void btnDisconnect_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
QuitChat();
btnDisconnect.Enabled = false;
btnConnect.Enabled = true;
btnSend.Enabled = false;
ns.Close();
clientsocket.Close();
receive.Abort();
connected = false;
lbChatters.Items.Clear();
statusBar1.Text = "已断开连接";
}
“发送”按钮:
private void btnSend_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
try
{
if(!privatemode)
{
string pubcommand = "CHAT|" + clientname +": "+ChatOut.Text + "/r/n";
Byte[] outbytes = System.Text.Encoding.ASCII.GetBytes(pubcommand.ToCharArray());
ns.Write(outbytes,0,outbytes.Length);
ChatOut.Text = "";
}
else
{
if(lbChatters.SelectedIndex == -1)
{
MessageBox.Show("请从列表中选择一个对象!","错误",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
string destclient = lbChatters.SelectedItem.ToString();
string command = "PRIV|" + clientname + "|: " + ChatOut.Text + "|" + destclient;
Byte[] outbytes = System.Text.Encoding.ASCII.GetBytes(command.ToCharArray());
ns.Write(outbytes,0,outbytes.Length);
ChatOut.Text = "";
}
}
catch(Exception)
{
MessageBox.Show("到服务器的连接丢失!","错误",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
ns.Close();
clientsocket.Close();
if(receive != null && receive.IsAlive)
receive.Abort();
connected = false;
statusBar1.Text = "已断开连接";
}
}
“悄悄话”按钮:
private void cbPrivate_CheckedChanged(object sender, System.EventArgs e)
{
if(cbPrivate.Checked)
{
privatemode = true;
lbChatters.SelectionMode = System.Windows.Forms.SelectionMode.One;
}
else
{
privatemode = false;
lbChatters.SelectionMode = System.Windows.Forms.SelectionMode.None;
}
}
全部源程序请参见NET.mjc/ChatClient