我对网络编程比较陌生。在过去的几天里,我做了大量的谷歌搜索和研究,并有一个聊天应用程序,可以让多个用户连接到服务器,并能够相互发送消息。
现在没有用于断开客户端的捕获或方法,我将在以后添加。但是现在,我希望在客户端表单的文本框中添加显示在线用户列表的功能。
当客户端连接到服务器时,服务器将此客户端添加到“clientList”。但是,我对如何将此列表发送给客户端有点困惑,但更重要的是,我如何让客户端认识到这不是常规消息,更多是客户列表。
我考虑过这样做,所以它用一个独特的字符串发送它并做一个if语句,但我知道有更好的方法。
在客户端代码上,我有一个后台工作程序,用于侦听来自服务器的数据。当然,如果我将列表序列化为二进制格式化程序,它将被我的“消息监听器”接收,程序将对什么是消息以及连接客户端的数据感到困惑。因此,我不确定如何区分这两者。
我绝不会要求你为我编码。我只是在寻找那些在该领域拥有更多智慧和经验的人的建议。如果我能就最好的方法得到一些指示,我会非常感激。我很感激你的时间。
客户端代码 -
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace socketClientForm
{
public partial class Form1 : Form
{
private static byte[] buffer = new byte[1024];
private static Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public string message = "";
public Form1()
{
InitializeComponent();
this.Text = "Client";
}
delegate void SetTextCallback();
private void SetText()
{
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { });
}
else
this.chatBox.AppendText(message);
}
private void LoopConnect()
{
int attempts = 0;
while (!clientSocket.Connected)
try
{
attempts++;
clientSocket.Connect(IPAddress.Parse(IPBox.Text), 8080);
}
catch (SocketException)
{
chatBox.Clear();
chatBox.AppendText("Connection attempts: " + attempts.ToString());
}
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(backgroundWorker1.RunWorkerAsync), clientSocket);
chatBox.Clear();
chatBox.AppendText("Connected \n");
}
private void submitButton_Click(object sender, EventArgs e)
{
if (!String.IsNullOrWhiteSpace(msgBox.Text))
{
string req = usernameBox.Text + ": " + msgBox.Text;
byte[] buffer = Encoding.ASCII.GetBytes(req);
clientSocket.Send(buffer);
msgBox.Text = "";
}
}
private void connectButton_Click(object sender, EventArgs e)
{
LoopConnect();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (clientSocket.Connected)
{
try
{
byte[] receivedBuf = new byte[1024];
int rec = clientSocket.Receive(receivedBuf);
byte[] data = new byte[rec];
Array.Copy(receivedBuf, data, rec);
message = Encoding.ASCII.GetString(data) + "\n";
SetText();
}
catch
{
}
}
}
}
}
服务器端代码 -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace socketServer
{
class Program
{
private static byte[] buffer = new byte[1024];
private static List<Socket> clientSockets = new List<Socket>();
private static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main(string[] args)
{
Console.Title = "Server";
SetupServer();
Console.ReadLine();
}
private static void SetupServer()
{
Console.WriteLine("Setting up server...");
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
serverSocket.Listen(1);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null);
}
private static void AcceptCallBack(IAsyncResult AR)
{
Socket socket = serverSocket.EndAccept(AR);
clientSockets.Add(socket);
Console.WriteLine("Client Connected");
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallBack), null);
}
private static void ReceiveCallBack(IAsyncResult AR)
{
Socket socket = (Socket)AR.AsyncState;
int received = socket.EndReceive(AR);
byte[] dataBuff = new byte[received];
Array.Copy(buffer, dataBuff, received);
string text = Encoding.ASCII.GetString(dataBuff);
Console.WriteLine("Text received: " + text);
byte[] data = Encoding.ASCII.GetBytes(text);
foreach (Socket client in clientSockets)
{
client.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), client);
}
//socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
//socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
}
private static void SendCallback(IAsyncResult AR)
{
Socket socket = (Socket)AR.AsyncState;
socket.EndSend(AR);
}
}
}
答案 0 :(得分:0)
如果使用普通字节和字符串消息,则很难导出消息传递协议。 你最好创建一个模型 - 比如
public class NetMessage{
public int MessageType{get;set;}
public dynamic Payload{get;set;}
}
因此,让映像MessageType 1成为您的身份验证请求。
就像是
{ "MessageType":"1", "PayLoad":{
"Username":"Admin",
"Password":"Password123"
}
}
您可以将其序列化为字符串并发送(通过Newtonsoft.Json) 或者,我更喜欢使用二进制格式化程序将对象直接转换为字节,然后通过网络发送字节。 发送序列化到字节格式的数据,比通过网络发送字符串信息稍微有效。
使用上述协议,您可以让服务器在MessageType上执行switch语句,然后以不同方式处理逻辑。
在您的问题中,您想发送已连接客户的列表吗?
使用类似MessageType 99的内容,并将Payload设置为客户端列表。 请记住,您无法序列化TcpClient对象并将其发送给远程用户,并期望该对象的功能类似于连接的TcpClient。 您最多可以发送远程IP和服务器连接的端口。 所以我建议发送一个代表这些数据的模型。
更新:
目前,您的后台工作人员正在接收数据并将其作为字节处理 - &gt;文本,然后在文本上直接执行业务逻辑。
您应该使用的是托管类型,而不是字符串类型。 字符串太低,你需要一些中间类型来帮助管理逻辑。
使用visual studio中的nuget包管理器安装Newtonsoft.Json (或JSON.Net有时也称为)
使用Newtonsoft,您可以执行以下操作。
给出一个看起来像这样的类
public class MessageClass
{
public int MessageType{get;set;}
public dynamic Payload{get;set;}
}
您可以执行以下操作
string content = "{\"MessageType\":\"2\",\"Payload\":\"blah\"}";
这是一个JSON格式的字符串,表示一个类实例。
在C#代码中,这个对象是这样的:
var message = new MessageClass();
message.MessageType=2;
message.Payload = "blah";
Newtonsoft为您提供的功能是将字符串转换为托管C#类型的能力。 例如:
请记住我们上面的字符串'content'?
var managedObject = JsonConvert.DeserializeObject<MessageClass>(content);
Console.WriteLine(managedObject.MessageType); // will; write 2
我建议的是,您的客户端和服务器通过JSON格式化对象进行通信,然后使您能够执行更高级的条件语句和更准确的断言。
Newtonsoft为您提供了两种关键方法。 记录在newtonsoft网站上。 newtonsoft.com/json/help/html/Methods_T_Newtonsoft_Json_JsonConvert.htm
将C#对象转换为字符串 JsonConvert.SerializeObject(object o); JsonConvert.DeserializeObject(String value);
对于deserialize方法,将要反序列化的类型放在T所在的位置。
例如:
MessageClass msg = JsonConvert.DeserializeObject<MessageClass>(content);