以前C/S通讯这一块都是基于套接字(Sockets)编程,但是面对业务逻辑复杂的C/S系统,通讯这一块往往都是被封装好了的,前几天写的一个小小窗体聊天程序,主要是练习一下C#封装的TcpListener、TcpClient以及NetworkStream的使用,直接看图上代码:
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; namespace ChartTestServer { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private NetworkStream networkStream; private StreamReader streamReader; private StreamWriter streamWriter; private Socket socketForClient; private TcpListener tcpListener; private Thread serverThread; // 启动服务线程 private Thread recvThread;// 接收信息线程 private void button1_Click(object sender, EventArgs e) { serverThread = new Thread(new ThreadStart(Listen)); serverThread.Start(); } //[STAThread] private void Listen() { try { Int32 port = Int32.Parse(txtPort.Text); IPAddress ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; tcpListener = new TcpListener(ipAddress, port); // 开始侦听 tcpListener.Start(); listBox1.Items.Add("服务已经启动!"); button1.Enabled = false; button2.Enabled = true; // 返回可以用以处理连接的Socket实例 socketForClient = tcpListener.AcceptSocket(); // 如果返回值是"true",则产生的套节字已经接受来自远方的连接请求 if (socketForClient.Connected) { button3.Enabled = true; listBox1.Items.Add("客户端成功连接上服务器!"); // 创建networkStream对象通过网络套节字来接受和发送数据 networkStream = new NetworkStream(socketForClient); // 从当前数据流中读取一行字符,返回值是字符串 streamReader = new StreamReader(networkStream); streamWriter = new StreamWriter(networkStream); recvThread = new Thread(new ThreadStart(RecvData)); recvThread.Start(); } } catch (Exception exc) { MessageBox.Show(exc.Message, "Server提示"); } } // 接收服务器发来的信息 private void RecvData() { string msg = streamReader.ReadLine(); // 如果不是接收到客户端发来的退出信息,则不断接收信息 while (!msg.Equals("exit")) { //statusChart1.Value = Convert.ToInt32(msg); listBox1.Items.Add("收到信息: " + msg); msg = streamReader.ReadLine(); } // 如果接收到客户的退出信息,则退出服务 button1.Enabled = true; button2.Enabled = false; button3.Enabled = false; listBox1.Items.Add("客户已经关闭"); listBox1.Items.Add("服务器关闭"); // 关闭资源 ReleaseResource(); } private void button3_Click(object sender, EventArgs e) { Thread sendThread = new Thread(new ThreadStart(SendData)); sendThread.Start(); } private void SendData() { if (txtMsg.Text != "") { listBox1.Items.Add("发送信息: " + txtMsg.Text); // 往当前的数据流中写入一行字符串 streamWriter.WriteLine(txtMsg.Text); // 刷新当前数据流中的数据 streamWriter.Flush(); txtMsg.Text = ""; // 清空 } else { MessageBox.Show("发送信息不能为空", "提示"); } } private void button2_Click(object sender, EventArgs e) { try { if (button3.Enabled == true) { // 停止服务后,允许 启动服务,但是不允许发送消息 button1.Enabled = true; button2.Enabled = false; button3.Enabled = false; string exitMsg = "exit"; // 要退出时,发送 exit 信息给客户端 streamWriter.WriteLine(exitMsg); // 刷新当前数据流中的数据 streamWriter.Flush(); listBox1.Items.Add("服务器关闭"); // 释放资源 recvThread.Abort(); ReleaseResource(); } else { // 停止服务后,允许 启动服务,但是不允许发送消息 button1.Enabled = true; button2.Enabled = false; button3.Enabled = false; listBox1.Items.Add("服务器关闭"); // 关闭TCP连接,关闭线程 tcpListener.Stop(); serverThread.Abort(); } } catch (Exception exc) { MessageBox.Show(exc.Message, "Server提示"); } } // 释放资源 private void ReleaseResource() { networkStream.Close(); streamReader.Close(); streamWriter.Close(); socketForClient.Shutdown(SocketShutdown.Both); socketForClient.Close(); tcpListener.Stop(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // 在未停止 服务 前,不允许关闭窗体 if (button1.Enabled == false) { MessageBox.Show("请先停止服务!", "Server提示"); e.Cancel = true; } } private void Form1_Load(object sender, EventArgs e) { // 在没有启动服务器之前,不允许停止服务以及发送信息 button2.Enabled = false; button3.Enabled = false; } }
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; namespace ChartTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private NetworkStream networkStream; private StreamReader streamReader; private StreamWriter streamWriter; private TcpClient myclient; private Thread recvThread; // 接收信息线程 private Thread sendThread; // 发送信息线程 private Thread serverThread;// 服务线程 private Thread st; private void button1_Click(object sender, EventArgs e) { serverThread = new Thread(new ThreadStart(Connection)); serverThread.Start(); } private void Connection() { /* try { IPAddress ipAddress = IPAddress.Parse(txtIP.Text); Int32 port = Int32.Parse(txtPort.Text); IPEndPoint ipe = new IPEndPoint(ipAddress, port); myclient = new TcpClient(); myclient.Connect(ipe); } catch { MessageBox.Show("没有连接到服务器!"); return; } */ try { IPAddress ipAddress = IPAddress.Parse(txtIP.Text); Int32 port = Int32.Parse(txtPort.Text); string hostName = Dns.GetHostEntry(ipAddress).HostName; myclient = new TcpClient(hostName, port); } catch { MessageBox.Show("没有连接到服务器!"); return; } listBox1.Items.Add("客户端成功连接上服务器!"); button1.Enabled = false; // 连接上了,不让按“连接”按钮 button2.Enabled = true; button3.Enabled = true; networkStream = myclient.GetStream(); streamReader = new StreamReader(networkStream); streamWriter = new StreamWriter(networkStream); // 创建接收信息线程,并启动 recvThread = new Thread(new ThreadStart(RecvData)); recvThread.Start(); } // 接收数据 private void RecvData() { string s = streamReader.ReadLine(); // 如果没接到服务器退出的消息,则继续接收信息 while (s!=null&&!s.Equals("exit")) { listBox1.Items.Add("收到信息:" + s); s = streamReader.ReadLine(); } // 收到服务器退出的消息,释放资源 listBox1.Items.Add("服务器关闭"); listBox1.Items.Add("客户端关闭"); button1.Enabled = true; button2.Enabled = false; button3.Enabled = false; // streamReader.Close(); // streamWriter.Close(); // networkStream.Close(); // myclient.Close(); ReleaseResouce(); } private void button2_Click(object sender, EventArgs e) { button1.Enabled = true; // 按了停止之后,“连接”按钮可以用,“发送”不能用 button2.Enabled = false; button3.Enabled = false; string exitMsg = "exit"; // 要退出时,发送 exit 信息给服务器 streamWriter.WriteLine(exitMsg); //刷新当前数据流中的数据 streamWriter.Flush(); listBox1.Items.Add("客户端关闭"); ReleaseResouce(); } private void button3_Click(object sender, EventArgs e) { // 启动发送线程 sendThread = new Thread(new ThreadStart(SendData)); sendThread.Start(); } // 发送数据 private void SendData() { // 发送信息不允许为空 if (txtMsg.Text == "") { MessageBox.Show("发送的信息不能为空!"); txtMsg.Focus(); return; } try { //往当前的数据流中写入一行字符串 streamWriter.WriteLine(txtMsg.Text); //刷新当前数据流中的数据 streamWriter.Flush(); listBox1.Items.Add("发送信息:" + txtMsg.Text); txtMsg.Text = ""; // 清空 } catch (Exception exc) { MessageBox.Show(exc.Message, "ClientClient提示"); } } private void Form1_Load(object sender, EventArgs e) { // 在未连接服务器前,不能发送信息和停止 button2.Enabled = false; button3.Enabled = false; } // 释放资源 private void ReleaseResouce() { networkStream.Close(); streamReader.Close(); streamWriter.Close(); if (sendThread!=null&&sendThread.IsAlive) { sendThread.Abort(); } if (recvThread.IsAlive) { recvThread.Abort(); } if (serverThread.IsAlive) { serverThread.Abort(); } myclient.Close(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // 如果是在连接中,则不允许关闭窗口 if (button1.Enabled == false) { MessageBox.Show("请先停止连接!", "ClientClient提示"); e.Cancel = true; } } private void button4_Click(object sender, EventArgs e) { // 启动发送线程 st = new Thread(new ThreadStart(SeData)); st.Start(); } // 发送数据 private void SeData() { Random r = new Random(); for (int i = 0; i < 10000; i++) { Thread.Sleep(400); try { //往当前的数据流中写入一行字符串 streamWriter.WriteLine(r.Next(80).ToString()); //刷新当前数据流中的数据 streamWriter.Flush(); listBox1.Items.Add("发送信息:" + r.Next(80).ToString()); } catch (Exception exc) { MessageBox.Show(exc.Message, "ClientClient提示"); } } } private void button5_Click(object sender, EventArgs e) { st.Abort(); } } }
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;