最近我们工作室的老师要过来检查了 算是正式大二了。这便是让我抛下医药项目,突击看了书本和网上的例子结合写的Tcp聊天工具。之前我对套接字还是很排斥的,看到就头疼 当初直接跳过 这次回过头来做这个项目也是对我自己的完善和检测。
先来看一下我的简单winform窗体的界面吧
* 注意:*
1首先注意的点就是 上面的Icon图标是可以改的,这样看起来好看点,这个也是我的队友告诉我的 我也是蠢的一比 啥也不知道
2还有标题 当初我本来是要用Paint绘制画板写的 ,后来发现可以用Label控件就ok
3.本文章的客户端是针对传送字符串操作的 和下面的服务器端有点冲突 因为 服务器端还可以传送文件等操作 所以 留意区别
TCP要用到的头文件
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Threading;
知识点
用到的知识点无非就是
1.Socket的套接字
2.线程(没有线程的话 当窗体在等待接收的时候 窗体是卡死的 不能进行其他操作 所以要对等待的事件进行后台线程操作)
3.skinEngine控件的皮肤控件 用来美化窗体(控件在其他文章里 自己找)
4键盘控制输入
5 首先要设置Socket GetSocket; 这个全局变量 不然容易忽视而造成无连接
6form窗体有个属性是AccepButton 绑定一个控件 比如button1 效果是如果按键盘的Enter相当于单击button1
7还有一个问题就是 当传输的内容过多 不进行操作的话 就会出现看不到消息 要手动拖动滑轮 解决方案如下:
单击richtextbox控件 进行操作
private void richTextBox2_TextChanged(object sender, EventArgs e)
{
richTextBox2.SelectionStart = richTextBox2.Text.Length;
richTextBox2.ScrollToCaret();//将控件内容滚到当前插入字符的位置
}
对窗体进行初始化 窗体加载的时候要 加载皮肤 。其次还要对线程的检查设置为false 否则 线程无法建通。最后 要对窗体接受键盘按键的值设置为true。
private void Form1_Load(object sender, EventArgs e)
{
this.skinEngine1.SkinFile = "Silver.ssk";
Control.CheckForIllegalCrossThreadCalls = false;
this.KeyPreview = true;
}
单击连接按钮的代码 try{}catch{}是为了防止窗体因为服务端断来连接而造成的窗体卡死
private void button1_Click(object sender, EventArgs e)
{
try
{
Socket GetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建了Socket连接
IPAddress IP = IPAddress.Parse(textBox1.Text);//获取ip地址
IPEndPoint point = new IPEndPoint(IP, Int32.Parse(label1.Text)); //是ip地址和端口号的集合
GetSocket.Connect(point);//创建连接 GetSocket和服务器端建立连接
Thread thread = new Thread(Recive);//创建线程将Recive函数作为传递给线程的委托
thread.IsBackground = true; //设置线程为后台线程
thread.Start(GetSocket);
}
catch { }
}
recive函数用来接受服务器端的消息
void Recive(object a)
{
GetSocket = a as Socket;
try
{
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 2];//传输的时候都是用Byte字节来传输的 这是2MB
int number = GetSocket.Receive(buffer);//number是实际接受的长度 receive是接受服务器端传送的消息
if (number == 0)
break; //这是防止意外的跳出
string ss = Encoding.UTF8.GetString(buffer, 0, number); //这是byte转换成string类型 0表示从buffer下标为0开始 ,number表示转化的长度
ShowThing(ss);
}
}
catch { }
}
这是 文本框结尾追加 为了代码清晰所以分开写
void ShowThing(string ss)
{
richTextBox2.AppendText(ss + "\n");
}
这是客户端发送给服务器端的按钮控件
private void button2_Click(object sender, EventArgs e)
{
try
{
byte[] buffer = new byte[1024 * 1024 * 2];
string name = "李四--->" + richTextBox1.Text;
buffer = Encoding.UTF8.GetBytes(name);
GetSocket.Send(buffer); //文本框转化byte的内容
ShowThing(name);
richTextBox1.Clear();//清空文本框
}
catch
{
}
}
这是窗体接受键盘按钮值的操作 在这里 发送按钮不用Enter 因为 如果这样子虽然也可以达到发送效果 但是文件框会换行 如果 case Key.S的话 也可以达到对应的效果,但是文本框结束后会残留S值 意思就是 虽然按键实现了效果但是按键的 值也施加给了text文本
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode) //按下的值
{
case Keys.F1: button1_Click(sender, e); break;//当按下F1的时候 系统就默认单击button1
case Keys.Escape: button3_Click(sender, e); break;
}
}
以上是客户端的操作 接下来是 服务器端的操作
这个是服务器端的界面 没有进行排版有点简陋
实现了 服务器端对多个不同的可以客户端进行收发的效果 还有传送文件的操作 而不是简单的
我就直接复制代码过来好了
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace WindowsFormsApplication12
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
private void ShowMsg(string str)
{
richTextBox1.AppendText(str + "\r\n");
}
private void button1_Click(object sender, EventArgs e)
{
try
{
Form2 xx =new Form2();
xx.Show();
Form2 xx1 = new Form2();
xx1.Show();
Form2 xx2 = new Form2();
xx2.Show();
Socket sockWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint point = new IPEndPoint(IPAddress.Any, int.Parse(textBox2.Text));
sockWatch.Bind(point); //监听程序
sockWatch.Listen(10);//等待加入的客户端 限制是10个以内
Thread thread = new Thread(Listen);
thread.IsBackground = true;
thread.Start(sockWatch);
}
catch
{
}
}
Socket socketSend;
Dictionary<string, Socket> SocketDic =new Dictionary<string, Socket>();//这个是为了 实现服务器端对多个客户端进行收发消息 名字对应连接
void Recive(object o)//监听等待进入的客户端
{
socketSend = o as Socket; //线程要用object 然后转换成需要的类型
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 2];
int r=socketSend.Receive(buffer);
if (r == 0)
break;
string str = Encoding.UTF8.GetString(buffer, 0, r);
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);//RemoteEndPoint获取socketSend终端的ip地址和端口号信息
}
}
void Listen(object o) //接受客户端传信息
{
try
{
Socket socketWatch = o as Socket;
while (true)
{
socketSend = socketWatch.Accept();
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + " ok");
string name = socketSend.RemoteEndPoint.ToString();
SocketDic.Add(name, socketSend);//键值对传值
comboBox1.Items.Add(name);
Thread thread = new Thread(Recive);
thread.IsBackground = true;
thread.Start(socketSend);
}
}
catch
{
}
}
//发送字符串消息这个实现了 判断传入的消息是字符串类型
private void button2_Click(object sender, EventArgs e)
{
byte[] buffer = Encoding.UTF8.GetBytes(richTextBox2.Text);
List<byte> list = new List<byte>();
list.Add(1);
list.AddRange(buffer);
byte[] newbuffer = list.ToArray();
string name=comboBox1.SelectedItem.ToString();
SocketDic[name].Send(newbuffer);
// MessageBox.Show(socketSend.RemoteEndPoint.ToString());
}
private void button3_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "选择打开文件";
ofd.Filter = "suoyouwenjian|*.*";
ofd.ShowDialog();
textBox3.Text = ofd.FileName;
}
private void button4_Click(object sender, EventArgs e)
{
string name = textBox3.Text;
using (FileStream fs = new FileStream(name,FileMode.OpenOrCreate,FileAccess.ReadWrite))
{
byte[] buffer = new byte[1024 * 1024 * 3];
int r= fs.Read(buffer,0,buffer.Length);
List<byte> list = new List<byte>();
list.Add(2);
list.AddRange(buffer);
byte[] newbuffer = list.ToArray();
SocketDic[comboBox1.SelectedItem.ToString()].Send(newbuffer);
}
}
//震动按钮
private void button5_Click(object sender, EventArgs e)
{
byte[] buffer=new byte[1];
buffer[0]=2;
SocketDic[comboBox1.SelectedItem.ToString()].Send(buffer);
}
}
}
接收器主要代码
void recive()
{
while (true)
{
byte[] buffer = new byte[1024*1024*2];
int r = sockSend.Receive(buffer);
if (r == 0)
break;
else if (buffer[0] == 1)
{
string ss = Encoding.UTF8.GetString(buffer,1,r-1);
if(ss!="")
showMsg(sockSend.RemoteEndPoint.ToString() + ":" + ss);
}
else if (buffer[0] == 2)
{
SaveFileDialog sdg = new SaveFileDialog();
sdg.Title = "请选择保存路径";
sdg.Filter="text|*.txt*";
sdg.InitialDirectory = @"C:\Users\蒋家聪\Desktop";
sdg.ShowDialog(this);//不加this会报错
using (FileStream write = new FileStream(sdg.FileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
write.Write(buffer, 1, r - 1);
MessageBox.Show("保存成功!");
}
}
}
}