Socket的通信方式:
Socket有TCP和UDP两种通信方式,我们可以根据具体的情况来选择。一般情况下,如果需要数据准确传输、不丢失,则选择TCP;反之,则选择UDP。
参考文章:
Socket的TCP和UDP连接
需要提前了解的Socket知识点:
- Client端输入的IP都是Server所在电脑的IP
- Server最好设置0.0.0.0这样无论迁移到哪个电脑上,都是那台电脑的IP
- Client和Server必须在同一个局域网之下,否则不能通信;如果要实现跨局域网通信,需要使用公网IP(通过阿里云等)
整个聊天室的代码分为两个模块:
- Server服务器模块:只能有一个,用来存储监听用户发送的内容
- Client客户模块:可以有多个,各个用户直接进行收发信息
以下是Server代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Server
{
class Program
{
static Socket socket = null;
static void Main(string[] args)
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Parse("0.0.0.0"), 60000));
socket.Listen(10);
Console.WriteLine("已启动侦听");
Task.Run(Connect);
Console.ReadLine();
}
static List<Socket> socketPool = new List<Socket>();
static Socket acceptSocket = null;
static void Connect()
{
while (true)
{
try
{
acceptSocket = socket.Accept();
var receiveSocket = acceptSocket;
Console.WriteLine($"已接受连接:{receiveSocket.RemoteEndPoint}");
socketPool.Add(receiveSocket);
Task.Run(Receive);
}
catch (Exception ex)
{
Console.WriteLine($"连接失败:{ex.Message}");
}
}
}
static void Receive()
{
var receiveSocket = acceptSocket;
while (true)
{
if (receiveSocket == null) continue;
if (!receiveSocket.Connected) continue;
try
{
byte[] buffer = new byte[1024];
var len = receiveSocket.Receive(buffer);
if (len < 1) continue;
var msg = Encoding.UTF8.GetString(buffer, 0, len);
Console.WriteLine($"来自 {receiveSocket.RemoteEndPoint} 的消息 {msg}");
var responseBuffer = Encoding.UTF8.GetBytes($"来自{receiveSocket.RemoteEndPoint} 的消息:{msg}");
// receiveSocket.Send(responseBuffer);
//清理失效的连接
for (int i = socketPool.Count - 1; i >= 0; i--)
{
if (socketPool[i] == null || !socketPool[i].Connected)
{
socketPool.RemoveAt(i);
}
}
//广播消息(除了发送消息方之外的,全部广播)
foreach (var s in socketPool)
{
if (s != receiveSocket)
{
s.Send(responseBuffer);
}
}
buffer = null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + "\n");
}
}
}
}
}
以下是Client代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
/// <summary>
/// 客户端
/// </summary>
namespace WpfApp1
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
Socket socket = null;
public MainWindow()
{
InitializeComponent();
btnConnect.Click += btnConnect_Click;
btnSend.Click += btnSend_Click;
Task.Run(Receive);
}
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
if (socket != null && socket.Connected)
{
ShowMessage($"无需重复连接");
return;
}
try
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ShowMessage($"LocalEndPoint={socket.LocalEndPoint},RemoteEndPoint={socket.RemoteEndPoint}");
var ipEndPoint = new IPEndPoint(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text));
socket.Connect(ipEndPoint);
if (socket.Connected)
{
ShowMessage($"成功连接到:{socket.RemoteEndPoint}");
}
}
catch (Exception ex)
{
ShowMessage($"连接失败:{ex.Message}");
socket = null;
}
}
private void btnSend_Click(object sender, RoutedEventArgs e)
{
if (!socket.Connected)
{
ShowMessage($"未连接,无法发送");
return;
}
try
{
if (rtxtSend.Text != "")
{
var buffer = Encoding.UTF8.GetBytes(rtxtSend.Text);
socket.Send(buffer);
ShowMessage($"发送到:{socket.RemoteEndPoint},消息:{rtxtSend.Text}");
}
rtxtSend.Text = null;
}
catch (Exception ex)
{
ShowMessage($"发送失败:{socket.RemoteEndPoint},{ex.Message}");
socket = null;
}
}
private void Receive()
{
while (true)
{
if (socket == null) continue;
if (!socket.Connected) continue;
try
{
byte[] buffer = new byte[1024];
if (buffer != null)
{
int len = socket.Receive(buffer);
if (len < 1) continue;
string msg = Encoding.UTF8.GetString(buffer, 0, len);
ShowMessage(msg);
}
buffer = null;
}
catch (Exception ex)
{
ShowMessage(ex.Message + "\n");
socket = null;
}
}
}
private void ShowMessage(string msg)
{
///这是Winform的用法
//子线程调用
//if (rtxtLog.InvokeRequired) //c#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的
//{
// rtxtLog.Invoke(new Action(() => rtxtLog.Text += msg + "\n"));
//}
//主线程调用
//else
//{
// rtxtLog.Text += msg + "\n";
//}
if (!CheckAccess()) //WPF使用Dispatcher控制对消息泵的访问,而不是让每个控件负责访问UI线程。
{
Dispatcher.Invoke(new Action(() => rtxtLog.Text += msg + "\n"));
}
//主线程调用
else
{
rtxtLog.Text += msg + "\n";
}
}
}
}