使用socket的流程图如下:
首先需要制作客户端和服务器端的界面
客户端:
服务器端:
先说服务器端的代码
首先写服务器端的开始检测按钮的代码:
private void button1_Click(object sender, EventArgs e)
{
//创建一个负责监听的socket
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//socket后的三个:interNetwor是你的ip版本中间是选择数据流还是字节报
//最后一个是流对应的tcp协议和报对应的udp协议,我选择的是tcp协议,安全稳定
//获取ip地址和端口号
IPAddress ip = IPAddress.Parse(textBox1.Text);
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(textBox2.Text));
//绑定端口号和ip地址
socketWatch.Bind(point);
ShowMsg("开始监听");
//设置监听队列
socketWatch.Listen(5);//最大监听数目,可以理解为最大连接数
//为新创建的连接创建一个socket
//创建一个新线程执行
Thread th = new Thread(Listen);//创建线程是为了防止程序假死
th.IsBackground = true;
th.Start(socketWatch);
}
接下来是监听的方法,需要占用一个线程来防止程序假死:
且每一次有电脑连接客户端,就会产生一个新的socketSend,旧的socketSend就会消失,想要保持连接我们就需要建立一个键值对集合将每一个连接的电脑的ip和端口记录下来,才能保持通信
void Listen(object o)
{
Socket socketWatch = o as Socket;
while (true)
{
Socket socketSend = socketWatch.Accept();//循环等待连接
//将远程链接过来的sock和保存
dicsocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
comboBox1.Items.Add(socketSend.RemoteEndPoint.ToString());
ShowMsg(socketSend.RemoteEndPoint.ToString() + "连接成功");
//连接成功后,还需要创建一个线程去循环等待客户端的消息
Thread th = new Thread(Recive);
th.IsBackground = true;
th.Start(socketSend);
}
}
textbox3控件显示方法+建立键值对集合:
void ShowMsg(string str)
{
textBox3.AppendText(str+"\r\n");
}
Dictionary<string, Socket> dicsocket = new Dictionary<string, Socket>();
//键值对集合用于存储ip地址,搭配comboBox控件使用
为了区分服务器端的消息究竟是文字还是文件抑或是震动,我们自己搭配集合写一个协议,将数据发送按钮的数据第一位加上表示,0表示是消息,1表示是文件,2表示是震动
发送按钮代码:
private void button4_Click(object sender, EventArgs e)
{
string str = textBox4.Text;
byte[] buffer = new byte[1024 * 1024 * 2];
buffer = Encoding.UTF8.GetBytes(str);//将字符转换成字节数组
List<byte> list = new List<byte>();//加标识
list.Add(0);
list.AddRange(buffer);
byte[] newBuffer = list.ToArray();
string ip = comboBox1.SelectedItem.ToString();
dicsocket[ip].Send(newBuffer);//发送消息
}
选择文件代码:
private void button2_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "请选择你要发送的文件";
ofd.Filter = "文本文件|*.txt";
ofd.InitialDirectory = @"C:\Users\14505\Desktop";
ofd.ShowDialog();
string str = ofd.FileName;//获得选择的文件的路径
textBox5.Text = str;
}
发送文件代码:
private void button3_Click(object sender, EventArgs e)
{
string path = textBox5.Text;//获得路径
using (FileStream fsRead = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read))
{
byte[] buffer = new byte[1024 * 1024 * 5];
int r= fsRead.Read(buffer, 0, buffer.Length);
List<byte> list = new List<byte>();
list.Add(1);//加标识
list.AddRange(buffer);
byte[] newbuffer = list.ToArray();//转换为字节数组
string ip = comboBox1.SelectedItem.ToString();
dicsocket[ip].Send(newbuffer, 0, r + 1, SocketFlags.None);//使用键值对获得客户端的ip
}
}
震动按钮:只需要发送一个字节就可以了,客户端收到后根据数据的首位就可以识别出来
private void button5_Click(object sender, EventArgs e)
{
byte[] buffer = new byte[1];
buffer[0] = 2;
string ip = comboBox1.SelectedItem.ToString();
dicsocket[ip].Send(buffer);
}
接下来是客户端代码:
老规矩,先使用socket,与之前区别不大,不过服务端要使用两个socket,而客户端只需要一个就够了
连接按钮的代码:
Socket socketSend;
private void button1_Click(object sender, EventArgs e)
{
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(textBox1.Text);
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(textBox2.Text));
socketSend.Connect(point);//连接指定的ip和端口
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":连接成功");
Thread th = new Thread(Recive);
th.IsBackground = true;
th.Start();
}
textbox3控件展示方法:
void ShowMsg(string str)
{
textBox3.AppendText(str + "\r\n");
}
接下来是发送信息按钮的代码:
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
string str = textBox4.Text;
byte[] buffer = Encoding.UTF8.GetBytes(str);
socketSend.Send(buffer);
}
接下来是客户端最难的接收信息的代码了,要对服务端的信息做一个判断,判断他是消息还是一个文件等,在客户端发送的代码中,我们是在字符数组首位做了标识的,现在只需要判断buffer【0】就可以了,在转换成字符格式时,我们可以从每个字符数组的第一位开始转换,共转换r-1个,可以免去不必要的麻烦。
void Recive()
{
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 2];
int r = socketSend.Receive(buffer);//接受信息的代码
if (buffer[0] == 0)//判断是否是信息
{
string str = Encoding.UTF8.GetString(buffer, 1, r - 1);//从第一位判断
ShowMsg(socketSend.RemoteEndPoint + ":" + str);
}
else if (buffer[0] == 1)
{
//SaveFileDialog sfd = new SaveFileDialog();
//sfd.Title = "请选择保存位置";
//sfd.Filter = "文本文件|.txt";
//sfd.InitialDirectory = @"C:\Users\14505\Desktop";
//sfd.ShowDialog(this);
//string path = sfd.FileName;
//using (FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
//{
// fsWrite.Write(buffer, 1, buffer.Length - 1);
//}
//MessageBox.Show("保存成功");
SaveFileDialog sfd = new SaveFileDialog();
sfd.Title = "请选择保存位置";
sfd.Filter = "文本文件|.txt";
sfd.InitialDirectory = @"C:\Users\14505\Desktop";
sfd.ShowDialog(this);
string path = sfd.FileName;
using (FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
{
fsWrite.Write(buffer, 1, r - 1);
}
MessageBox.Show("保存成功");
}
else if (buffer[0] == 2)
{
ZD();//震动方法,在后面
}
}
}
振动的方法:我选择的只是闪烁位置的方案,也可以选择搭配timer控件,有更好的效果。
void ZD()
{
for (int i = 0; i < 500; i++)
{
this.Location = new Point(200, 200);
this.Location = new Point(280, 280);
}
}
程序运行:
可以同时连接多个客户端并保持联系
发送文件和保存文件等也都可以使用,就不再截图了
最常见的一个异常展示:
碰到这个异常的解决方案就是使用环回地址127.0.0.1,该异常是因为这个电脑的ip地址和端口没有服务端,大概率与防火墙的设置有关。
涉及到网络连接的代码很可能会蹦出来一些很奇怪的异常,建议在软件中多多使用try{}和catch{}来解决异常
附上一些socket的知识吧
tcp的三次握手:
socket的一般应用模式:
流式socket和数据报式socket: