文章目录
1、创建WinForm应用程序
这里就不细说创建方法了,直接上创建完成的效果图吧
主入口两个按钮事件:
/// <summary>
/// 打开客户端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_OpenClient_Click(object sender, EventArgs e)
{
SocketClient socketClient = new SocketClient();
socketClient.ShowDialog();
}
/// <summary>
/// 打开服务端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_OpenService_Click(object sender, EventArgs e)
{
SocketService socketService = new SocketService();
socketService.ShowDialog();
}
2、服务端界面绘制
其中txt_MessageBox与txtSendBox属性Multiline=true
3、客户端界面绘制
4、服务端功能实现
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Socket通信聊天室
{
public partial class SocketService : Form
{
public SocketService()
{
InitializeComponent();
//多线程编程中,如果子线程需要使用主线程中创建的对象和控件,最好在主线程中体现进行检查取消
CheckForIllegalCrossThreadCalls = false;
}
/// <summary>
/// 界面加载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SocketService_Load(object sender, EventArgs e)
{
//获取Ip大概率位【0.0.0.0】表示任何IP
//在服务端连接的时候先查看本机Ipv4地址
var ip = IPAddress.Any.ToString();
this.txt_IpAddress.Text = ip;
}
/// <summary>
/// 创建一个字典,用来存储记录服务器与客户端之间的连接(线程问题)
/// </summary>
Dictionary<string, System.Net.Sockets.Socket> clientList = new Dictionary<string, System.Net.Sockets.Socket>();
private void btn_Create_Click(object sender, EventArgs e)
{
System.Threading.Thread myServer = new System.Threading.Thread(MySocket);
//设置这个线程是后台线程
myServer.IsBackground = true;
myServer.Start();
}
/// <summary>
/// 发送
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Send_Click(object sender, EventArgs e)
{
if (this.txtSendBox.Text != "")
{
SendMsg(this.txtSendBox.Text);
this.txtSendBox.Text = "";
}
}
/// <summary>
/// 创建连接的方法
/// </summary>
public void MySocket()
{
//1.创建服务器端电话
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
//2.创建手机卡
IPAddress iP = IPAddress.Parse(this.txt_IpAddress.Text);
IPEndPoint endPoint = new IPEndPoint(iP, int.Parse(this.txt_Port.Text));
//3.将电话卡插进电话中
server.Bind(endPoint);
//4.开始监听电话卡
//同一时刻内允许同时加入链接的最大数量
server.Listen(20);
this.txt_MessageBox.AppendText("服务器已经成功开启! \r\n");
//5.等待来电接电话
while (true)
{
//接受接入的一个客户端
Socket connectClient = server.Accept();
if (connectClient != null)
{
string infor = connectClient.RemoteEndPoint.ToString();
clientList.Add(infor, connectClient);
this.txt_MessageBox.AppendText(infor + "加入服务器! \r\n");
///服务器将消息发送至客服端
string msg = $"[{infor}]已成功进入到聊天室!";
SendMsg(msg);
//每有一个客户端接入时,需要有一个线程进行服务
Thread threadClient = new Thread(ReciveMsg);
threadClient.IsBackground = true;
//设置这个线程中的通信对象是对应的Socket和客户端Socket进行通信
threadClient.Start(connectClient);
}
}
}
/// <summary>
/// 不停尝试接收服务器接收到客户端发送的消息
/// </summary>
/// <param name="o">客户端</param>
public void ReciveMsg(object o)
{
Socket client = o as Socket;
while (true)
{
try
{
///定义服务器接收的字节大小
byte[] arrMsg = new byte[1024 * 1024];
///接收到的信息大小(所占字节数)
int length = client.Receive(arrMsg);
if (length > 0)
{
string recMsg = Encoding.UTF8.GetString(arrMsg, 0, length);
//获取客户端的端口号
IPEndPoint endPoint = client.RemoteEndPoint as IPEndPoint;
//服务器显示客户端的端口号和消息
this.txt_MessageBox.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}[{ endPoint.Port.ToString()}]:{recMsg} \r\n");
//服务器发送接收到的客户端信息给客户端
SendMsg($"[{endPoint.Port.ToString()}]{recMsg}");
}
}
catch (Exception)
{
///关闭客户端
client.Close();
///移除添加在字典中的服务器和客户端之间的线程
clientList.Remove(client.RemoteEndPoint.ToString());
}
}
}
/// <summary>
/// 服务器发送消息,
/// </summary>
public void SendMsg(string str)
{
///遍历出字典中的所有线程
foreach (var item in clientList)
{
byte[] arrMsg = Encoding.UTF8.GetBytes(str);
///获取键值,发送消息
item.Value.Send(arrMsg);
}
}
}
}
5、客户端功能实现
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Socket通信聊天室
{
/// <summary>
/// 客户端
/// </summary>
public partial class SocketClient : Form
{
public SocketClient()
{
InitializeComponent();
}
/// <summary>
/// Socket连接
/// </summary>
private System.Net.Sockets.Socket socket;
/// <summary>
/// 加入聊天室
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Join_Click(object sender, EventArgs e)
{
// Internet 协议、字节流、IPv4连接
socket = new System.Net.Sockets.Socket(
System.Net.Sockets.AddressFamily.InterNetwork,
System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.IP);
System.Net.IPAddress ip = System.Net.IPAddress.Parse(this.txt_IpAddress.Text);
System.Net.IPEndPoint point = new System.Net.IPEndPoint(ip, int.Parse(this.txt_Port.Text));
socket.Connect(point);
System.Threading.Thread thread = new System.Threading.Thread(ReciveMsg);
thread.IsBackground = true;
thread.Start(socket);
}
/// <summary>
/// 不停的尝试接收消息
/// 接收信息
/// </summary>
/// <param name="o">客户端</param>
public void ReciveMsg(object o)
{
System.Net.Sockets.Socket client = o as System.Net.Sockets.Socket;
while (true)
{
try
{
///定义客户端接收到的信息大小
byte[] arrList = new byte[1024 * 1024];
///接收到的信息大小(所占字节数)
int length = client.Receive(arrList);
this.txt_MessageBox.AppendText($"{DateTime.Now}:{Encoding.UTF8.GetString(arrList, 0, length)} \r\n");
}
catch (Exception)
{
client.Close();
}
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Send_Click(object sender, EventArgs e)
{
if (txtSendBox.Text != "")
{
byte[] arrMsg = Encoding.UTF8.GetBytes(txtSendBox.Text);
socket.Send(arrMsg);
}
}
}
}
6、调试
6.1 开始执行(不调试)
同一个WinForm项目同时开启两个运行结果需在VS中添加【开始执行(不调试)】功能
6.2 服务端启动时
服务端启动时自动获取到的IP地址【0.0.0.0】并不是一个真实的的IP地址,它表示本机中所有的IPV4地址。
故客户端连接时输入的IP地址应为本机的IPv4地址,可通过cmd命令查看
6.3 调试结果
7、Demo下载
完整Demo下载:C# WinForm使用Socket通信搭建简易聊天室