声明:第四部分是讲诉如何直接使用服务端和客户端,如果不想复制,只想查看效果可以执行跳转。
如果你将服务端挂在在自己的电脑上,则朋友在和你同一个WIFI下才能进行聊天。
如果将服务端挂载到服务器上,则可以在不同的地方聊天。(当然客户端的设置的连接服务端的IP要设置为公网IP)
1.Socket服务端代码
1.1 新建一个控制台程序(类似于命令行界面)
1.2 在Nuget中导入Socket
1.3 新建一个CS文件,复制以下代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
namespace SocketServer
{
public class SocketService
{
private Socket socket = null;
private string IP = string.Empty;
private int Port = 0;
private byte[] buffer = new byte[1024 * 1024 * 2];
private Dictionary<string, Socket> clientDic = new Dictionary<string, Socket>();
public List<string> UserList = new List<string>();
/// <summary>
/// 构造函数
/// </summary>
/// <param name="ip">监听的IP</param>
/// <param name="port">监听的端口</param>
public SocketService(string ip, int port)
{
this.IP = ip;
this.Port = port;
}
public SocketService(int port)
{
this.IP = "0.0.0.0";
this.Port = port;
}
public void SocketListen()
{
try
{
//1.0 实例化套接字(IP4寻找协议,流式协议,TCP协议)
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2.0 创建IP对象
IPAddress address = IPAddress.Parse(IP);
//3.0 创建网络端口,包括ip和端口
IPEndPoint endPoint = new IPEndPoint(address, Port);
//4.0 绑定套接字
socket.Bind(endPoint);
//5.0 设置最大连接数
socket.Listen(int.MaxValue);
WriteLog.Write("服务器已开启!");
Console.WriteLine("服务器已开启!");
//6.0 开始监听
Thread thread = new Thread(ListenClientConnect);
thread.Start();
}
catch (Exception ex)
{
WriteLog.Write(ex.Message);
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// 监听客户端连接
/// </summary>
private void ListenClientConnect()
{
try
{
while (true)
{
//Socket创建的新连接
Socket clientSocket = socket.Accept();
//连接成功后会往下走
EndPoint clientipe = clientSocket.RemoteEndPoint;
//打印日志
WriteLog.Write($"接收客户端【{clientipe}】连接...");
Console.WriteLine($"接收客户端【{clientipe}】连接...");
string IP = clientipe.ToString();
//通过IP绑定每个socket
clientDic.Add(clientipe.ToString(), clientSocket);
WriteLog.Write($"用户【{clientipe}】上线");
Console.WriteLine($"用户【{clientipe}】上线");
UserList.Add(clientipe.ToString());
string Users = string.Empty;
foreach (var item in UserList)
{
Users = item + "," + Users;
}
foreach (var kvp in clientDic)
{
kvp.Value.Send(Encoding.UTF8.GetBytes($"{clientipe},进入聊天"));
kvp.Value.Send(Encoding.UTF8.GetBytes($"Users,{Users}"));
}
Thread thread = new Thread(ReceiveMessage);
thread.Start(clientSocket);
}
}
catch (Exception ex)
{
WriteLog.Write(ex.Message);
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// 接收客户端消息
/// </summary>
/// <param name="socket">来自客户端的socket</param>
private void ReceiveMessage(object socket)
{
try
{
Socket clientSocket = socket as Socket;
while (true)
{
try
{
//获取从客户端发来的数据
int length = clientSocket.Receive(buffer);
string stringFromBytes = Encoding.UTF8.GetString(buffer, 0, length);
WriteLog.Write($"接收客户端消息:{stringFromBytes}");
Console.WriteLine($"接收客户端消息:{stringFromBytes}");
EndPoint clientipe = clientSocket.RemoteEndPoint;
foreach (var kvp in clientDic)
{
kvp.Value.Send(Encoding.UTF8.GetBytes($"{clientipe},{stringFromBytes}"));
}
}
catch (Exception ex)
{
WriteLog.Write(ex.Message);
Console.WriteLine(ex.Message);
//一般服务端关闭后会报错所以这里下线后断开Socket并修改该IP状态
EndPoint clientipe = clientSocket.RemoteEndPoint;
clientDic.Remove(clientipe.ToString());
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
WriteLog.Write($"用户【{clientipe}】下线");
Console.WriteLine($"用户【{clientipe}】下线");
UserList.Remove(clientipe.ToString());
string Users = string.Empty;
foreach (var item in UserList)
{
Users = Users + "," + item;
}
foreach (var kvp in clientDic)
{
kvp.Value.Send(Encoding.UTF8.GetBytes($"Users,{Users}"));
kvp.Value.Send(Encoding.UTF8.GetBytes($"{clientipe},退出聊天"));
}
}
}
}
catch (Exception ex)
{
WriteLog.Write(ex.Message);
Console.WriteLine(ex.Message);
}
}
}
}
其中WriteLog.Write()是自定义的日志存储方法,可以不用删掉
2.客户端代码
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using COM;
using SocketServer;
using System.Windows;
using System.Xml.Linq;
using COM.Common;
using SocketClient.Models;
using SocketServer.Common;
using SocketClient.Common;
using SocketClient.Views;
using System.Collections.Generic;
using System.Timers;
namespace SocketClient.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private System.Timers.Timer timer { get; set; }
public Socket sockets;
private byte[] buffer = new byte[1024 * 1024 * 2];
public string _SendMessage;
public string SendMessage
{
get { return _SendMessage; }
set { _SendMessage = value; OnPropertyChanged("SendMessage"); }
}
public ObservableCollection<ChatHistory> _Result;
public ObservableCollection<ChatHistory> Result
{
get { return _Result; }
set { _Result = value; OnPropertyChanged("Result"); }
}
public ObservableCollection<User> _ListUser = new ObservableCollection<User>();
public ObservableCollection<User> ListUser
{
get { return _ListUser; }
set { _ListUser = value; OnPropertyChanged("ListUser"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public DelegateCommand SendCommand { get; set; }
public DelegateCommand ShrinkCommand { get; set; }
public DelegateCommand CloseCommand { get; set; }
public DelegateCommand MenuCommand { get; set; }
public DelegateCommand KeyEventCommand { get; set; }
public MainWindowViewModel()
{
SendCommand = new DelegateCommand(SendEven);
ShrinkCommand = new DelegateCommand(ShrinkEven);
CloseCommand = new DelegateCommand(CloseEven);
MenuCommand = new DelegateCommand(MenuEven);
KeyEventCommand = new DelegateCommand(SendEven);
Result = new ObservableCollection<ChatHistory>();
ClientConnect();
}
private void MenuEven(object Win)
{
//UserWindow win = new UserWindow();
//win.ShowDialog();
}
/// <summary>
/// 关闭
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private void CloseEven(object Win)
{
sockets.Shutdown(SocketShutdown.Both);
sockets.Close();
Window window = Win as Window;
window.Close();
window.WindowState = WindowState.Minimized;
}
/// <summary>
/// 缩小
/// </summary>
/// <exception cref="NotImplementedException"></exception>
private void ShrinkEven(object Win)
{
Window window = Win as Window;
window.WindowState = WindowState.Minimized;
}
/// <summary>
/// 发送消息
/// </summary>
private void SendEven()
{
try
{
string sendMessage = string.Format(SendMessage);
sockets.Send(Encoding.UTF8.GetBytes(sendMessage));
SendMessage = string.Empty;
}
catch (Exception ex)
{
WriteLog.Write(ex.Message);
}
}
/// <summary>
/// 连接Socket
/// </summary>
public void ClientConnect()
{
/// <summary>
/// 开启服务,连接服务端
/// </summary>
try
{
//1.0 实例化套接字(IP4寻址地址,流式传输,TCP协议)
sockets = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2.0 创建IP对象
IPAddress address = IPAddress.Parse(Publics.ServerIPAddress);
//3.0 创建网络端口包括ip和端口
IPEndPoint endPoint = new IPEndPoint(address, 8080);
//固定端口
sockets.Bind(new IPEndPoint(IPAddress.Parse("0.0.0.0"), 9999));
//4.0 建立连接
sockets.Connect(endPoint);
Console.WriteLine("连接服务器成功");
//Publics.ClientName = sockets.LocalEndPoint.ToString();
//Publics.IP = sockets.LocalEndPoint.ToString();
//开启线程接收数据
Thread thread = new Thread(Message);
thread.Start();
}
catch (Exception ex)
{
sockets.Shutdown(SocketShutdown.Both);
sockets.Close();
Console.WriteLine(ex.Message);
WriteLog.Write(ex.Message);
}
}
/// <summary>
/// 接收数据
/// </summary>
public void Message()
{
try
{
//5.0 接收数据
while (true)
{
int lengths = sockets.Receive(buffer);
string stringFromBytes1 = Encoding.UTF8.GetString(buffer, 0, lengths);
string[] splitString = stringFromBytes1.Split(',');
if (splitString.Length > 0)
{
if (!string.IsNullOrEmpty(splitString[0]))
{
if (splitString[0] == "Users")
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
ListUser = new ObservableCollection<User>();
for (int i = 1; i < splitString.Length; i++)
{
User user = new User()
{
Name = splitString[i],
};
ListUser.Add(user);
}
}));
}
else
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
Result.Add(new ChatHistory() { UserName = splitString[0], Message = splitString[1] });
}));
}
}
else
{
WriteLog.Write("服务器传入的第一条数据为空");
}
}
else
{
WriteLog.Write("服务器传入的数据为空");
}
}
}
catch (Exception ex)
{
WriteLog.Write(ex.Message);
}
}
}
}
客户端代码我只放置了MVVM框架中的ViewModel中的代码,界面代码请看本文最末的Gitee链接
以上就是主要内容,剩余内容自己研究。
3.运行展示
3.1 服务器
3.2客户端
4.直接使用
如果想直接使用服务器和客户端,也可以跳过开发过程
服务器启动流程
客户端启动
需要修改连接服务端的IP地址,使用Win+R打开运行窗口,输入CMD 进入命令行界面。输入ipconfig
获取到对应的IP
然后进入这个地方,用记事本打开.config文件
将此地方的IP改为自己放置服务端的IP
然后打开SocketClient运行程序进入聊天室界面