用Winform实现Socket网络通信
一、Socket
概念:通常称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或计算机之间的通信;客户端如果要连接服务器,首先要知道服务器的IP地址,但服务器中有很多应用程序,光知道IP地址是不够的,因为客户端不知道连接哪一个程序,所以还需要知道一个服务器的端口号。有了服务器的IP地址和端口号,才能建立Socket连接。
二、传输过程中要有传输协议:
TCP特点:安全稳定,不会发生数据丢失,效率低,要求必须有一个服务器。
经历三次握手过程:1)客户端请求;2)服务器响应;3)客户端得知服务器响应,开始传输。
UDP特点:效率高,但有可能会发生数据丢失(不稳定)。不用经过服务器响应是否有空闲接受消息,自主传输。
举例:视频聊天时,TCP肯定会很清晰,但有可能会卡,因为服务器可能会忙;而UDP不会很清晰,但不会卡,所以视频聊天一般使用UDP协议(参考https://blog.csdn.net/tufeiax/article/details/45591917)
三、具体实现方式
服务器Sever端:
1)首先创建socket对象负责监听,并获取本机IP地址,再创建端口号对象将IP地址传入进去
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(textBox_ip.Text);
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(textBox_port.Text));
2)绑定监听,参数为端口号对象point
socket.Bind(point);
label1.Text = "监听服务已启动!";
3)设置监听以及等待队列
socket.Listen(10);
4)设置接收客户端的连接(注意:要写在while(true)循环中,因为要一直监听,并且创建后台新进程来执行,不止一个客户端要连接服务器,要不断创建负责通信的socket对象socketConnect)
/// <summary>
/// 接收客户端的连接
/// </summary>
public Socket socketConnect;
private void Listen(object o)
{
try
{
Socket socket = o as Socket;
while (true)
{
//负责监听的Socket来接受客户端的连接,创建跟客户端通信的Socket
socketConnect = socket.Accept();
dicList.Add(socketConnect.RemoteEndPoint.ToString(), socketConnect);
listBoxCoustomerList.Items.Add(socketConnect.RemoteEndPoint.ToString());
textboMsg.AppendText(socketConnect.RemoteEndPoint.ToString() + " 上线啦\r\n");
Thread th = new Thread(Receive);
th.IsBackground = true;
th.Start(socketConnect);
}
}
catch (Exception ex)
{
MessageBox.Show("客户端连接失败," + ex.Message);
}
}
5)接收客户端发来的消息
/// <summary>
/// 接收客户端发来的消息
/// </summary>
/// <param name="o"></param>
private void Receive(object o)
{
try
{
Socket socketReceive = o as Socket;
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 5];
int r = socketReceive.Receive(buffer);
if (r == 0)
{
break;
}
textboMsg.AppendText(socketReceive.RemoteEndPoint.ToString() + ":" + Encoding.Default.GetString(buffer, 0, r) + "\r\n");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
6)选择一个客户端给其发送信息
/// <summary>
/// 给客户端发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendMsg_Click(object sender, EventArgs e)
{
try
{
string SendMess = textBoxSendMsg.Text;
byte[] buffer = Encoding.Default.GetBytes(SendMess);
List<byte> sendList = new List<byte>();
sendList.Add(0);
sendList.AddRange(buffer);
byte[] newBuffer = sendList.ToArray();
string point=null;
if (listBoxCoustomerList.SelectedItem!= null)
{
point = listBoxCoustomerList.SelectedItem.ToString();
dicList[point].Send(newBuffer);
}
else
{
MessageBox.Show("请选择一个客户端发消息!!!", "Error");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
7)给全部客户端发送消息
/// <summary>
/// 给全部客户端发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAllSend_Click(object sender, EventArgs e)
{
try
{
string SendMess = textBoxSendMsg.Text;
byte[] buffer = Encoding.Default.GetBytes(SendMess);
List<byte> sendList = new List<byte>();
sendList.Add(2);
sendList.AddRange(buffer);
byte[] newBuffer = sendList.ToArray();
foreach (var item in listBoxCoustomerList.Items)
{
dicList[item.ToString()].Send(newBuffer);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
要是想要给客户端发送图片或者震动等,可以将发送的数据转为字节,然后创建byte的泛型集合,在集合首位添加数字(例如:单独发送信息是0,全部发送是1,发送图片是2,发送抖动是3),再将字节数组传入添加到结合中,然后客户端那边则以穿过的字节首位数字进行辨别
客户端Client
1)创建负责通信的socket,获取要连接的服务器IP和端口号
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(textBoxIp.Text);
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(textBoxPort.Text));
2)建立连接
socket.Connect(point);
3)接收服务端发来的消息,因为要不停接收消息,所以要写在while循环中,并且要创建后台新的线程来执行
Thread th = new Thread(ReceiveMess);
th.IsBackground = true;
th.Start();
/// <summary>
/// 接收服务器发来的消息
/// </summary>
/// <param name="o"></param>
private void ReceiveMess()
{
try
{
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 5];
int r = socket.Receive(buffer);
if (r == 0)
{
break;
}
if (buffer[0] == 0)
{
string RMess = Encoding.Default.GetString(buffer, 1, r - 1);
textboMsg.AppendText(socket.RemoteEndPoint.ToString() + ":" + RMess + "\r\n");
}
else if (buffer[0] == 2)
{
string RMess = Encoding.Default.GetString(buffer, 1, r - 1);
textboMsg.AppendText(socket.RemoteEndPoint.ToString() + ":" + RMess + "(此消息为群发消息,请注意查收)\r\n");
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
4)发送消息给服务器
/// <summary>
/// 发送消息给服务器
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendMsg_Click(object sender, EventArgs e)
{
byte[] bytes = Encoding.Default.GetBytes(textBoxSendMsg.Text);
socket.Send(bytes);
}
一个简易的Socket网络通信就实现了