我有一个套接字连接,我通过这个套接字发送数据。我连接的服务器回答每次正确的数据发送。我收到了消息,所以我得到了收到的每条消息的答案。有时,服务器喜欢保留消息几秒钟,或者以不同的顺序发送。我的解决方案是生成一个线程并让它围绕一个接收函数旋转。但是,在MSDN上使用套接字示例我很适合。它们使用简单的do / while循环结构。当我这样做时,我得到了混乱的回复和/或不完整的数据。这是一个家庭作业,所以我必须手工编写客户端,而不是只使用更简单的解决方案。这段代码可能有问题吗?我一直盯着它看,我想我错过了一些简单的东西:
private static void ReceiveThread(Socket sock, ReceiverClass rc)
{
// Create a socket and pass in parameter converted from object socket
int receivedBytes = 0;
do
{
// receive data from socket
receivedBytes = sock.Receive(rc.buffer);
byte[] formattedMsg = new byte[receivedBytes];
Array.Copy(rc.buffer, formattedMsg, receivedBytes);
rc.sb.Append("<LF><CR>" + System.Text.Encoding.ASCII.GetString(formattedMsg) + "\r\n");
}
while (receivedBytes > 0);
}
编辑,添加产生接收线程的功能。 (这是太长了,但是当我让愚蠢的事情发挥作用时,我计划让它变得漂亮):
public void SendData(Socket sock)
{
// Set socket timeout
sock.ReceiveTimeout = 4000;
// Prepare file for IO operations
string path = @"c:\Logs\Lab2.Scenario3.WurdingerO.txt";
StreamWriter logWrite = File.AppendText(path);
// Get local ip address:
IPAddress ip = Dns.GetHostAddresses(Dns.GetHostName()).Where(address => address.AddressFamily == AddressFamily.InterNetwork).First();
string portNum = ((IPEndPoint)sock.LocalEndPoint).Port.ToString();
// response time for scenario 2 and 3
int responseTime = 0;
// Set up Stopwatch to keep track of time
Stopwatch stpWatch = new Stopwatch();
stpWatch.Start();
// setup for logging class
ReceiverClass rc = new ReceiverClass();
// setup receiving thread
Thread receiveThread = new Thread(delegate()
{
ReceiveThread(sock, rc);
});
receiveThread.Start();
// Counter to call client operations
for (int i = 0; i < MESSAGE_COUNT; i++)
{
string msTime = Convert.ToString(stpWatch.ElapsedMilliseconds);
if (msTime.Length > 10)
{
string newMSTime = "";
for (int t = 9; t >= 0; t++)
{
newMSTime += msTime[t];
}
msTime = newMSTime;
}
Classes.RequestBuilder reqB = new Classes.RequestBuilder();
byte[] sendMsg;
switch (scenarioNo)
{
case 1:
sendMsg = reqB.MessageBuildScenarioOne(sock, msTime,
ip.ToString(), portNum, serverPort, serverIP, i);
break;
case 2:
// set up response time delay
switch (i)
{
case 1:
responseTime = 1000;
break;
case 3:
responseTime = 3000;
break;
default:
responseTime = 0;
break;
}
sendMsg = reqB.MessageBuildScenarioTwo(sock, msTime,
ip.ToString(), portNum, serverPort, serverIP, i, responseTime);
break;
case 3:
// set up response time delay
switch (i)
{
case 1:
responseTime = 1000;
break;
case 3:
responseTime = 3000;
break;
default:
responseTime = 0;
break;
}
sendMsg = reqB.MessageBuildScenarioThree(sock, msTime,
ip.ToString(), portNum, serverPort, serverIP, i, responseTime);
break;
default:
sendMsg = reqB.MessageBuildScenarioOne(sock, msTime,
ip.ToString(), portNum, serverPort, serverIP, i);
break;
}
try
{
sock.Send(sendMsg);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
// Socket shutdown
sock.Shutdown(SocketShutdown.Send);
receiveThread.Join();
sock.Shutdown(SocketShutdown.Receive);
string date = System.DateTime.Now.ToString("MMddyyyy");
string time = System.DateTime.Now.ToString("HHmmss");
logWrite.Write(rc.sb.ToString());
logWrite.Write(date + "|" + time + "|0|0|");
// Close log file
logWrite.Close();
System.Windows.Forms.MessageBox.Show("Finished");
}
编辑: 我在发送操作后放了一个睡眠定时器,修复了我遇到的问题。 谢谢!
答案 0 :(得分:0)
我认为我有类似的问题。
我使用Socket类发送JSON字符串,突然JSON.NET会抛出异常。我检查了字符串,我看到正在处理2个JSON(根)对象。然后我把一个最小的例子拼凑在一起,发现套接字会隐藏*任何未读的数据(未读=没有.Receive已被调用,而.Receive将清除存储)。
*藏匿:我不确定这是否是正确的术语。这对我来说很有意义。
这是我的测试。如果要运行它,请创建一个新的测试类并安装XUnit。
/// <summary>
/// Testing Net Sockets..
/// </summary>
/// <author>Jeff Hansen</author>
public class SocketTests
{
/// <summary>
/// The local endpoint for the server. There's no place like 127.0.0.1
/// </summary>
private IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1337);
/// <summary>
/// Tests that a call to .Receive will return all unread data.
/// </summary>
/// <author>Jeff Hansen</author>
[Fact]
public void ReceiveWillReadAllUnreadData()
{
// We expect to receive double data.
const string expected = "HELLOHELLO";
const string dataToSend = "HELLO";
// Start the server in a background task.
// We do this because the receive call is blocking.
var bgTask = Task.Factory.StartNew(() =>
{
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
// Bind to the endpoint and start listening.
server.Bind(endpoint);
server.Listen(10); // 10 is max allowed connections in queue (I think)
// Client connected, receive data
var connectingClient = server.Accept();
// So low-level we even get to allocate room for the data buffer manually.
var buf = new byte[1024];
// Hangs until data flows.
var sz = connectingClient.Receive(buf); // This is the size of the data we just received.
var data = Encoding.ASCII.GetString(buf, 0, sz); // We use the size to only grab what we need from the buffer.
Console.WriteLine("Data received: {0}", data);
// This is going to pass because we sent the data twice,
// and the call to Receive would not be able to complete in time
// for it to clear before more data becomes available.
Assert.Equal(expected, data);
/*
* BAM! Theory proven. We seriously had issues
* because we didn't understand how it worked. This is why you usually end
* your transmission with a newline.
*/
Console.WriteLine("Server closing");
server.Close();
}
catch (Exception e)
{
// Make sure we close the server.
server.Close();
throw;
}
});
// Create a client socket and connect it to the server.
// The server thread should have started it up by now.
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(endpoint);
// Get the bytes of our string
var buffer = Encoding.ASCII.GetBytes(dataToSend);
// Send it out twice. This happens faster than the server will process it,
// so the data is stacking up. You would THINK that .Receive would
// simply return the first data sent to it, and the next time .Receive is called
// return the next. But that's not how it works, apparently.
client.Send(buffer);
client.Send(buffer);
// Wait for the server to finish whatever it's doing.
try
{
// We give it 3000ms to complete.
bgTask.Wait();
}
catch (AggregateException ag)
{
// Throw any esceptions that were thrown in the background thread.
ag.Handle(ex => { throw ex; });
}
// Close the client socket.
client.Close();
}
}
我们通过用换行符分隔我们的数据来解决它。
有趣的事实:我在搞乱TcpClient,发现如果我没有给流写一个换行符,它就不会发送任何东西 - 它只会挂在那里。可能是一个本地化的问题。有人关心这个吗?