一、UI界面的设置
1.三个button控件,使用了Click()事件。
2.三个GroupBox控件,能够在一定区域显示字符,如:服务器。
3.两个RichTextBox控件,用于显示输入和接收的数据。
4.一个Timer定时器控件,用于接收数据的处理
二、服务器实现代码
1.相关变量
//定义的类内变量
private TcpListener Listener;
private Socket SocketClient;
private NetworkStream NetStream;
private StreamReader ServerReader;
private StreamWriter ServerWriter;
private Thread Thread;
2.核心代码
2.1运行的线程函数
public void TCPListener()
{
//在这里填入你的ip地址
this.Listener = new TcpListener(IPAddress.Parse("192.168.31.62"), 8000);
Listener.Start();
button1.Enabled = false;
this.Text = "服务器正在运行中。。。";
while (true)
{
try
{
SocketClient = Listener.AcceptSocket(); //等待客户端的socket
NetStream = new NetworkStream(SocketClient); //有客户端连接成功后,创建网络流
ServerReader = new StreamReader(NetStream); //读数据
ServerWriter = new StreamWriter(NetStream); //写数据
}
catch
{
}
}
}
该函数为线程的执行函数,不断地等待客户端的socket连接,并接收数据。
该函数在按键按下之后赋予一个新的线程,并且按键在按下后就已经锁定了,即服务器只需运行一个线程即可,而客户端可以有多个线程运行连接至服务器。
2.2 接收函数
public void GetMessage()
{
//网络流不为空并且有可用数据
if (NetStream != null && NetStream.DataAvailable)
{
richTextBox1.AppendText(DateTime.Now.ToString());//显示时间
richTextBox1.AppendText(" 客户端说:\n");
richTextBox1.AppendText(ServerReader.ReadLine() + "\n");
//设置下拉框
richTextBox1.SelectionStart = richTextBox1.Text.Length;
richTextBox1.Focus();
richTextBox2.Focus();
}
}
该函数在定时器的中断触发函数中运行,在网络流中有数据时将其打印在接收框控件中。
3.按键触发函数
3.1 连接服务器
private void button1_Click(object sender, EventArgs e)
{
this.Thread = new Thread(new ThreadStart(TCPListener));
this.Thread.Start();
}
开启线程,线程运行函数设置为TCPListener(),具体函数在2.1
3.2 发送数据
private void button3_Click(object sender, EventArgs e)
{
try
{
if (richTextBox2.Text.Trim() != "")
{
//信息写入流
ServerWriter.WriteLine(richTextBox2.Text);
ServerWriter.Flush();
//显示在文本框上。
richTextBox1.AppendText(DateTime.Now.ToString());
richTextBox1.AppendText(" 服务器说:\n");
richTextBox1.AppendText(richTextBox2.Text + "\n");
richTextBox2.Clear();
//滚动条
richTextBox1.SelectionStart = richTextBox1.Text.Length;
richTextBox1.Focus();
richTextBox2.Focus();
}
else
{
MessageBox.Show("信息不能为空!", "服务器消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
richTextBox2.Focus();
return;
}
}
catch
{
MessageBox.Show("客户端连接失败……", "服务器消息", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
用户单击发送时,根据输入在文本框中的数据,发送至客户端,并在信息接收框中显示。
3.3 清空数据
private void button2_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
}
清空接收框中的数据。
4.其他模块
4.1构造函数
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
4.2窗体相关事件
private void Form1_Load(object sender, EventArgs e)
{
this.richTextBox1.ReadOnly = true; //只读
this.timer1.Enabled = true; //定时器使能
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult dr = MessageBox.Show("关闭服务器将无法接收到来自客户端的数据,你确定要关闭吗?", "服务器信息", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (dr == DialogResult.Yes)
{
Listener.Stop();
this.Thread.Abort();
e.Cancel = false;
}
else
{
e.Cancel = true;
}
}
在窗体的事件函数中,可以做一些初始化的操作,并且在关闭窗体时关闭套接字和线程,否则将出现异常错误。
具体可以查看我的一篇博客
4.3定时器事件
private void timer1_Tick(object sender, EventArgs e)
{
//接收数据,处理数据
GetMessage();
}
调用数据接收函数GetMessage(),不断处理接收到的数据。
三、客户端实现代码
1.阐述
客户端代码实现和服务器类似,不过需要选择连接服务器的IP和port。
**PS:该服务器和客户端代码运行在同一局域网下,即使用相同的路由器或者热点,使用ipconfig在控制台查看IP,填入相关位置即可。
2.核心代码
public void GetConn()
{
CheckForIllegalCrossThreadCalls = false;
while (true)
{
try
{
TcpClient = new TcpClient(textBox1.Text.Trim(), Convert.ToInt32(textBox2.Text.Trim()));
Stream = TcpClient.GetStream();
//创建读写流
ClientReader = new StreamReader(Stream);
ClientWriter = new StreamWriter(Stream);
textBox1.Enabled = false;
button1.Enabled = false;
this.Text = "客户端 " + "与" + textBox1.Text.Trim() + "连接成功!";
return;
}
catch (Exception e)
{
textBox1.Enabled = true;
button1.Enabled = true;
MessageBox.Show(e.ToString());
return;
//MessageBox.Show("连接失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
该函数为客户端的线程执行函数,使用TcpClient类。
其余大致和服务器相同,了解了服务器后再编写客户端其实就是按图索骥的过程里。
如果你还不明白,可以点击这里,这里有我上传的资源,完整的工程。
祝诸位学习愉快!