c# - C# Socket 只能发送一次
问题描述
我在 Unity 中创建服务器时遇到问题。我的代码非常接近这些异步示例:
目前,服务器将毫无问题地启动,并且客户端可以连接。然后客户端可以发送 1 条消息并接收 1 条响应。在此之后,发送不再起作用。
我的控制台输出如下所示: https ://imgur.com/a/THJyWLI
如果有人知道这有什么问题,我将非常感激。
代码(我为代码质量道歉,这是因为我一直在努力让它工作)
服务器:
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System;
using UnityEngine;
public class TCP_Server : ServerType {
#region Threads
private Thread receiveThread;
private List<ThreadTime> sendThreads;
private void FlushThreads() {
if (sendThreads == null) {
return;
}
for (int i = 0; i < sendThreads.Count; i++) {
if (sendThreads[i].Done()) {
sendThreads.RemoveAt(i);
i--;
}
}
}
#endregion
public ManualResetEvent allDone = new ManualResetEvent(false);
private Packet testPacket = new Packet(0, 0, 0, "Server response template");
private byte[] TestData() {
List<Packet> p = new List<Packet>();
p.Add(testPacket);
return Packet.ToByteData(p);
}
private Socket listener;
static int Main(string[] args) {
TCP_Server s = new TCP_Server();
s.StartServer();
return 0;
}
public override void StartServer() {
base.StartServer();
sendThreads = new List<ThreadTime>();
receiveThread = new Thread(new ThreadStart(StartServerThread));
receiveThread.IsBackground = true;
receiveThread.Start();
}
public override void StopServer() {
base.StopServer();
}
public void StartServerThread() {
MonoBehaviour.print("Creating server");
//IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPHostEntry ipHostInfo = Dns.GetHostEntry("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Server.PORT);
listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try {
listener.Bind(localEndPoint);
listener.Listen(100);
while (true) {
allDone.Reset();
MonoBehaviour.print("Starting to listen");
listener.BeginAccept(new AsyncCallback(ReceiveData), listener);
allDone.WaitOne();
}
} catch (System.Exception e) {
MonoBehaviour.print(e);
}
}
private void ReceiveData(IAsyncResult ar) {
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
TcpStateObject state = new TcpStateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, TcpStateObject.bufferSize, 0, new AsyncCallback(ReadCallback), state);
}
private void ReadCallback(IAsyncResult ar) {
TcpStateObject state = (TcpStateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0) {
state.SaveBuffer(bytesRead);
if (state.EndOfData()) {
MonoBehaviour.print("Server received data: " + state.data.Count);
List<Packet> reqs = Packet.ToPacketData(state.ToByteArray());
foreach (Packet p in reqs) {
requests.Add(p);
}
Send(handler, TestData());
} else {
handler.BeginReceive(state.buffer, 0, TcpStateObject.bufferSize, 0, new AsyncCallback(ReadCallback), state);
}
} else {
}
}
private void Send(Socket handler, byte[] data) {
FlushThreads();
ThreadTime t = ThreadTime.New(delegate () { SendThread(handler, data); });
sendThreads.Add(t);
t.thread.Start();
}
private void SendThread(Socket handler, byte[] data) {
handler.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar) {
try {
Socket handler = (Socket)ar.AsyncState;
int bytesSent = handler.EndSend(ar);
//handler.Shutdown(SocketShutdown.Both);
//handler.Close();
} catch (Exception e) {
MonoBehaviour.print(e.ToString());
}
}
}
客户:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
public class TCP_Client : ClientType {
private Thread responseThread;
private List<ThreadTime> sendThreads;
private void FlushThreads() {
if (sendThreads == null) {
return;
}
for (int i = 0; i < sendThreads.Count; i++) {
if (sendThreads[i].Done()) {
sendThreads.RemoveAt(i);
i--;
}
}
}
private ManualResetEvent connectDone = new ManualResetEvent(false);
private ManualResetEvent sendDone = new ManualResetEvent(false);
private ManualResetEvent receiveDone = new ManualResetEvent(false);
private Socket client;
public override void SendRequests() {
FlushThreads();
ThreadTime t = ThreadTime.New(new ThreadStart(SendRequestsThread));
sendThreads.Add(t);
t.thread.Start();
}
public override void Connect() {
base.Connect();
sendThreads = new List<ThreadTime>();
responseThread = new Thread(new ThreadStart(ConnectThread));
responseThread.IsBackground = true;
responseThread.Start();
}
public void ConnectThread() {
MonoBehaviour.print("Connecting to server...");
try {
//IPHostEntry ipHostInfo = Dns.GetHostEntry("");
IPHostEntry ipHostInfo = Dns.GetHostEntry("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, Server.PORT);
client = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
MonoBehaviour.print("Client connect status: " + client.Connected);
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
MonoBehaviour.print("Client connect status: " + client.Connected);
} catch (Exception e) {
MonoBehaviour.print(e);
}
}
public override void Disconnect() {
base.Disconnect();
Socket client = null;
if (client == null) {
return;
}
client.Shutdown(SocketShutdown.Both);
client.Close();
client = null;
}
private void SendRequestsThread() {
MonoBehaviour.print("Sending " + requests.Count + " requests");
byte[] data = Packet.ToByteData(requests);
requests.Clear();
Send(client, data);
sendDone.WaitOne();
Receive(client);
receiveDone.WaitOne();
}
private void ConnectCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
MonoBehaviour.print($"Socket connected to {client.RemoteEndPoint.ToString()}");
// Signal that the connection has been made.
connectDone.Set();
} catch (Exception e) {
MonoBehaviour.print(e.ToString());
}
}
private void Receive(Socket client) {
try {
// Create the state object.
TcpStateObject state = new TcpStateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, TcpStateObject.bufferSize, 0, new AsyncCallback(ReceiveCallback), state);
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private void ReceiveCallback(IAsyncResult ar) {
try {
// Retrieve the state object and the client socket
// from the asynchronous state object.
TcpStateObject state = (TcpStateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.SaveBuffer(bytesRead);
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, TcpStateObject.bufferSize, 0, new AsyncCallback(ReceiveCallback), state);
} else {
state.EndOfData();
MonoBehaviour.print("Client received data: " + state.data.Count);
List<Packet> ress = Packet.ToPacketData(state.ToByteArray());
foreach (Packet p in ress) {
responses.Add(p);
}
receiveDone.Set();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
private void Send(Socket client, byte[] data) {
MonoBehaviour.print("Client sends data: " + data.Length);
client.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), client);
}
private void SendCallback(IAsyncResult ar) {
try {
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
MonoBehaviour.print("Send start: " + client.Connected);
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
// Signal that all bytes have been sent.
sendDone.Set();
MonoBehaviour.print("Send done: " + bytesSent);
} catch (Exception e) {
MonoBehaviour.print(e.ToString());
}
}
}
解决方案
我决定使用现有的网络库 (NetworkComms.net) 而不是编写自己的服务器。
推荐阅读
- javascript - 没有获取节点对象的对象属性
- linux - 根据 sacct 数据选择 slurm 工作
- sql-server - .net core api中的连接字符串应该是什么才能连接到dockerized sql express?
- python-3.x - 如何在 shell 脚本中切换 virtualenv?
- hyperledger - Hyperledger Composer:使用 FROM 生成 QUERY
- excel - 编译错误:插入 excel 公式的代码导致的语法错误
- python - 如何通过 Python 使用批量转录 API
- javascript - 使用 onclick 时,Electron 似乎找不到我的功能
- javascript - 画外音未在行选择中读取表格行的 aria-label 的内容
- javascript - 如何更改pdf的名称?