Socket 网络编程基础
Socket 不仅用于聊天应用程序,也是学习 .NET 网络编程的重要基础。本文演示如何实现服务端和客户端互发信息,以及服务端向多个客户端发送信息。
Socket 网络编程实战教程,涵盖 C# WinForms 环境下服务端监听、多客户端连接管理、消息收发及断开检测的实现逻辑。通过逐步完善代码,演示了从单连接循环接收数据到多线程处理多客户端通信的过程,解决了线程跨调用异常、空字节发送及客户端断开通知等常见问题,提供了完整的 TCP Socket 通信参考方案。
Socket 不仅用于聊天应用程序,也是学习 .NET 网络编程的重要基础。本文演示如何实现服务端和客户端互发信息,以及服务端向多个客户端发送信息。
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
// 创建监听用的 socket
private void btnStartMonitor_Click(object sender, EventArgs e)
{
// 监听的 IP 地址
IPAddress ip = IPAddress.Parse(txtIP.Text);
// 监听的 IP 地址和端口号
IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPortNumber.Text));
// 监听用的 socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 绑定监听的 IP 地址和端口号
socket.Bind(point);
// 监听端口,监听队列的长度
socket.Listen(10);
// 点击之后,禁用掉按钮
btnStartMonitor.Enabled = false;
// 提示用户程序开始运行了
ShowMsg("开始运行了...");
// 监听端口,如果有客户端连接,创建通信用 socket
// 用线程来解决窗体假死的问题
Thread th = new Thread(Listen);
th.IsBackground = true; // 后台线程
th.Start(socket);
}
// 监听是否有客户端连接
void Listen(object o)
{
// 监听用的 socket
Socket socket = o as Socket;
// 监听端口
Socket connSocket = socket.Accept();
// 显示远程连接的 IP 地址和端口号
ShowMsg(connSocket.RemoteEndPoint.ToString());
}
// 在文本框显示信息
void ShowMsg(string msg)
{
txtShowMessage.AppendText(msg + "\r\n");
}
上述代码仅支持单客户端连接,存在局限性。需完善为循环接收连接。
void Listen(object o)
{
Socket socket = o as Socket;
while (true)
{
Socket connSocket = socket.Accept();
ShowMsg(connSocket.RemoteEndPoint.ToString());
byte[] buffer = new byte[1024 * 1023];
int num = connSocket.Receive(buffer);
string str = Encoding.UTF8.GetString(buffer, 0, num);
ShowMsg(str);
}
}
void Listen(object o)
{
Socket socket = o as Socket;
while (true)
{
Socket connSocket = socket.Accept();
ShowMsg(connSocket.RemoteEndPoint.ToString());
while (true)
{
byte[] buffer = new byte[1024 * 1023];
int num = connSocket.Receive(buffer);
string str = Encoding.UTF8.GetString(buffer, 0, num);
ShowMsg(str);
}
}
}
void Listen(object o)
{
Socket socket = o as Socket;
while (true)
{
Socket connSocket = socket.Accept();
ShowMsg(connSocket.RemoteEndPoint.ToString());
Thread th = new Thread(RecMsg);
th.IsBackground = true;
th.Start(connSocket);
}
}
void RecMsg(object o)
{
Socket connSocket = o as Socket;
while (true)
{
byte[] buffer = new byte[1024 * 1024];
int num = connSocket.Receive(buffer);
string str = Encoding.UTF8.GetString(buffer, 0, num);
ShowMsg(connSocket.RemoteEndPoint.ToString() + ":" + str);
}
}
int count = 0;
void Listen(object o)
{
Socket socket = o as Socket;
while (count < 1000)
{
Socket connSocket = socket.Accept();
ShowMsg(connSocket.RemoteEndPoint.ToString());
Thread th = new Thread(RecMsg);
th.IsBackground = true;
th.Start(connSocket);
count++;
}
}
void RecMsg(object o)
{
Socket connSocket = o as Socket;
while (true)
{
byte[] buffer = new byte[1024 * 1024];
int num = connSocket.Receive(buffer);
if (num == 0)
{
ShowMsg(connSocket.RemoteEndPoint.ToString() + ":退出");
connSocket.Shutdown(SocketShutdown.Receive);
connSocket.Close();
break;
}
string str = Encoding.UTF8.GetString(buffer, 0, num);
ShowMsg(connSocket.RemoteEndPoint.ToString() + ":" + str);
}
}
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
Socket socket;
private void btnConnect_Click(object sender, EventArgs e)
{
IPAddress ip = IPAddress.Parse(txtIP.Text);
IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPortNumber.Text));
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Connect(point);
ShowMsg("连接成功");
}
catch (Exception ex)
{
ShowMsg(ex.Message);
}
}
void ShowMsg(string msg)
{
txtShowMessage.AppendText(msg + "\r\n");
}
private void btnSendMessage_Click(object sender, EventArgs e)
{
if (socket != null)
{
byte[] buffer = Encoding.UTF8.GetBytes(txtInputMessage.Text);
socket.Send(buffer);
}
else
{
MessageBox.Show("请先连接");
}
}
void RecMsg(object o)
{
Socket connSocket = o as Socket;
while (true)
{
byte[] buffer = new byte[1024 * 1024];
int num = -1;
try
{
num = connSocket.Receive(buffer);
if (num == 0)
{
ShowMsg(connSocket.RemoteEndPoint.ToString() + ":退出");
connSocket.Shutdown(SocketShutdown.Receive);
connSocket.Close();
break;
}
string str = Encoding.UTF8.GetString(buffer, 0, num);
ShowMsg(connSocket.RemoteEndPoint.ToString() + ":" + str);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
break;
}
}
}
private void btnSendMessage_Click(object sender, EventArgs e)
{
if (connSocket != null)
{
byte[] buffer = Encoding.UTF8.GetBytes(txtInputMessage.Text);
try
{
connSocket.Send(buffer);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
}
}
}
void RecMsg()
{
while (true)
{
byte[] buffer = new byte[1024 * 1024];
try
{
int num = socket.Receive(buffer);
string str = Encoding.UTF8.GetString(buffer, 0, num);
ShowMsg(str);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
break;
}
}
}
Thread th = new Thread(RecMsg);
th.IsBackground = true;
th.Start();
Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
int count = 0;
void Listen(object o)
{
Socket socket = o as Socket;
while (count < 100)
{
count++;
Socket connSocket = socket.Accept();
ShowMsg(connSocket.RemoteEndPoint.ToString());
string ipport = connSocket.RemoteEndPoint.ToString();
cboUser.Items.Add(ipport);
dic.Add(ipport, connSocket);
Thread th = new Thread(RecMsg);
th.IsBackground = true;
th.Start(connSocket);
}
}
private void btnSendMessage_Click(object sender, EventArgs e)
{
string ipport = cboUser.Text;
if (!string.IsNullOrEmpty(ipport))
{
byte[] buffer = Encoding.UTF8.GetBytes(txtInputMessage.Text);
try
{
Socket connSocket = dic[ipport];
connSocket.Send(buffer);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
}
}
else
{
MessageBox.Show("请选择客户端");
}
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online