Socket网络编程

一、Socket网络编程

1、概念:Socket    英文愿意是孔或插座。作为BSD UNIX的进程通信机制,取后一种意思,通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。

2、理解:

客户端如果要链接服务器,首先要知道服务器的IP地址,但服务器中有很多应用程序,光知道IP地址是不够的,因为客户端不知道链接哪一个程序,所以还需要知道一个服务器的端口号。有了服务器的IP地址和端口号,才能建立Socket连接。

3、传输的过程中还需要一个协议,协议有两种:TCP、 UDP

TCP特点:安全稳定,不会发生数据丢失,效率低,要求必须有一个服务器。因为要经历三次握手过程:1)客户端请求; 2)服务器响应  3)客户端得知服务器响应。 

然后才开始传输

UDP特点:效率高,但有可能会发生数据丢失(不稳定)。不用经过服务器响应是否有空闲接受消息,自主传输

举例:视频聊天时,TCP肯定会很清晰,但有可能会卡,因为服务器可能会忙;而UDP不会很清晰,但不会卡,所以视频聊天一般使用UDP协议

4、下面开始创建服务器:

1) 创建一个负责监听的Socket对象,负责监听某个IP地址和端口号

/当点击监听的时候,在服务器端创建一个负责监听IP地址跟端口号的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2)获取本机的IP地址,并创建端口号对象,将获取的IP地址传参进去。

IPAddress ip = IPAddress.Any; //IPAddress.Parse(txtServer.Text);
//创建端口号
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
3)设置绑定端口号,调用监听Socket对象的绑定方法,参数为前面创建的端口号对象

//监听绑定
socketWatch.Bind(point);
ShowMsg("监听成功");
4)设置监听:调用Socket的监听方法,参数是等待队列

socketWatch.Listen(10); //等待队列
5)创建负责通信的Socket对象,等待客户端的连接。注意:监听要写在while(true)里面,因为需要一直监听,并且需要创建后台新线程来执行它

//等待客户端的连接,并且创建一个负责通信的Socket 它代表客户端的Socket
Socket socketSend = socketWatch.Accept();
//如果连接成功,将”IP+:连接成功“追加到信息显示文本框中
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":连接成功");
这时候可以将IP地址和通信Socket存储成一个键值对集合:

//将此次连接的Socket存入键值对集合
dicSocket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);

6)连接成功后,接收客户端发过来的消息。注意:接收也要写在while(true)里面,需要一直接收客户端发过来的消息,并且需要创建后台新线程来执行它
byte[] buffer = new byte[1024 * 1024 * 2]; //创建一个字节数组,用来接收数据
int r = socketSend.Receive(buffer); //此方法实现接收客户端发过来的消息,并将其收入参数字节数组中,返回接收字节数
if (r == 0)
return;
string str = Encoding.UTF8.GetString(buffer, 0, r); //将字节数组转回字符串
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str);
7)实现给指定客户端发送消息:

string str = txtMsg.Text.Trim(); //获取将要发送的消息
byte[] buffer = Encoding.UTF8.GetBytes(str); //转成字节数组
string ip = comboBox1.SelectedItem.ToString(); //获得指定的客户端通信Socket
dicSocket[ip].Send(buffer); //发送消息

5、开始创建客户端
1)创建负责通信的Socket:

//创建负责通信的Socket
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2)获取将要连接的服务器的IP和程序的端口:
//获得要连接的服务器程序的IP地址和端口号
IPAddress ip = IPAddress.Parse(txtServer.Text.Trim());
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text.Trim()));
3)建立连接:
socketSend.Connect(point); //建立连接
ShowMsg("连接成功");
4)给服务器发送消息:
string str = txtMessage.Text.Trim(); //获取要发送的消息
byte[] buffer = Encoding.UTF8.GetBytes(str); //转成字节数组
socketSend.Send(buffer); //发送字节数组
5)不停的接收服务器发来的消息:注意:接收也要写在while(true)里面,需要一直接收客户端发过来的消息,并且需要创建后台新线程来执行它
byte[] buffer = new byte[1024 * 1024 * 2]; //定义一个字节数组
int r = socketSend.Receive(buffer); //接收字节数组放入buffer中,返回接收的字节数
if (r == 0) //如果没有信息接收,结束该循环
return;
string str = Encoding.UTF8.GetString(buffer, 0, r); //将接受的字节数组转成字符串
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str); //显示出来
//开启新线程,不停的接收服务器发来的消息
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start();

6、判断服务器发送的消息的文件格式,并区分接收。协议为: 文字消息---0; 文件----1;震动----2
1)文字消息:

服务器发送前添加协议

//给buffer添加一个协议
List<byte> list = new List<byte>();
list.Add(0); //首位赋值0
list.AddRange(buffer); //将buffer全部赋值给list,从第二位开始
byte[] newBuffer = list.ToArray(); //转回字节数组,返回一个新数组
将发送消息的参数改为新字节数组:
dicSocket[ip].Send(newBuffer); //发送消息

客户端接收前判断协议
//判断协议,区分处理
if(buffer[0]==0) //如果服务器发送的是文字消息! 注意:解码时要从第二位开始解码了,将0改为1,解码字节数-1
{
string str = Encoding.UTF8.GetString(buffer, 1, r-1); //将接受的字节数组转成字符串
ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + str); //显示出来
}

2)文本接收、
服务器:

打开对话框

OpenFileDialog open = new OpenFileDialog();
open.InitialDirectory = @"C:Users ufeiaxDesktop";
open.Title = "请选择要打开的文件";
open.Filter = "文本文件|*.txt|图片文件|*.jpg|所有文件|*.*";
open.ShowDialog();
发送选中的文本:
//获得要发送文件的全路径
string path = txtPath.Text.Trim();
//读取文件,并将其转换为字节数组
using (FileStream fsread = new FileStream(path, FileMode.Open, 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();
//注意:因为要多发一个字节,所有发送字节数为r+1
dicSocket[comboBox1.SelectedItem.ToString()].Send(newBuffer, 0, r + 1, SocketFlags.None);
}
客户端:
else if(buffer[0] ==1)
{
SaveFileDialog save = new SaveFileDialog();
save.InitialDirectory = @"C:Users ufeiaxDesktop";
save.Title = "请选择要保存的文件";
save.Filter = "文本文件|*.txt|图片文件|*.jpg|所有文件|*.*";
save.ShowDialog(this);
string path = save.FileName;
//保存文件
using(FileStream fsWrite = new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write))
{
fsWrite.Write(buffer, 1, r - 1);
}
MessageBox.Show("保存成功");
}

3)震动消息
服务器:

byte[] buffer = new byte[1];
buffer[0] = 2;
dicSocket[comboBox1.SelectedItem.ToString()].Send(buffer);
客户端:
for (int i = 0; i < 200; i++)
{
this.Location = new Point(200, 200);
this.Location = new Point(250, 250);
}



原文地址:https://www.cnblogs.com/aijiao/p/9961893.html