客户服务器套接字C#

时间:2017-01-11 10:32:37

标签: c# .net sockets

我正在使用socket C#。我已经使用套接字实现了客户端服务器应用程序,但问题是客户端没有收到服务器发送的所有数据。

这是客户端应用程序代码。我应该怎么做才能收到服务器发送的所有数据?

strRecieved = "";
Socket soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001);
soc.Connect(endPoint);
byte[] msgBuffer = Encoding.Default.GetBytes(path);
soc.Send(msgBuffer, 0, msgBuffer.Length, 0);
byte[] buffer = new byte[2000];
int rec = soc.Receive(buffer);

strRecieved = String.Format(Encoding.Default.GetString(buffer));

2 个答案:

答案 0 :(得分:1)

首先。如果您正在实现某种流功能(tcp / udp / file),则应考虑使用某种协议

什么是协议?这只是流式传输数据时使用的方案。例如:

[4Bytes - length] [lengthBytes - message] [1Byte - 终止指标]

了解协议,您可以简单地读取所有传入的字节:

byte[] buffer = new byte[4];
stream.ReadBytes(buffer, 0, 4); // cast that to int and read the rest

int packetLen = BitConverter.ToInt32(buffer, 0);
buffer = new byte[packetLen];
stream.ReadBytes(buffer, 0, buffer.Length); // all bytes that was sent

请记住,在发送邮件之前,您必须减去长度中的4个字节。

修改

有关如何使用共享协议发送和接收数据的简单示例。

// sender.cs
string _stringToSend = "some fancy string";
byte[] encodedString = Encoding.UTF8.GetBytes(_stringToSend);
List<byte> buffer = new List<byte>();
buffer.AddRange(BitConverter.GetBytes(encodedString.Length));
buffer.AddRange(encodedString);
netStream.WriteBytes(buffer.ToArray(), 0, buffer.Count);
// netStream sent message in protocol [@LEN - 4Bytes][@MSG - @LENBytes]
// simply speaking something like: 5ABCDE

// receiver.cs
byte[] buffer = new byte[sizeof(int)];
netStream.ReadBytes(buffer, 0, buffer.Length);
// receiver got the length of the message eg. 5
int dataLen = BitConverter.ToInt32(buffer, 0);
buffer = new byte[dataLen];
// now we can read an actual message because we know it's length
netStream.ReadBytes(buffer, 0, buffer.Length);
string receivedString = Encoding.UTF8.GetString(buffer);
// received string is equal to "some fancy string"

简化

此技术强制您使用所需的协议,在此示例中将是:

前4个字节sizeof(int)表示传入数据包的长度 每个字节都是你的数据包直到结束。

现在你应该制作ProtocolHelper对象:

public static class ProtocolHelper
{
    public byte[] PackIntoProtocol(string message)
    {
        List<byte> result = new List<byte>();
        byte[] messageBuffer = Encoding.UTF8.GetBytes(message);
        result.AddRange(BitConverter.GetBytes(messageBuffer.Length), 0); // this is the first part of the protocol ( length of the message )
        result.AddRange(messageBuffer); // this is actual message
        return result.ToArray();
    }

    public string UnpackProtocol(byte[] buffer)
    {
        return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
    }
}

现在(取决于您选择从网络中读取的方法),您必须发送和接收消息。

// sender.cs
string meMessage = "network message 1";
byte[] buffer = ProtocolHelper.PackIntoProtocol(meMessage);
socket.Send(buffer, 0, buffer.Length, 0);

// receiver.cs
string message = string.Empty;
byte[] buffer = new byte[sizeof(int)]; // or simply new byte[4];
int received = socket.Receive(buffer);
if(received == sizeof(int))
{
    int packetLen = BitConverter.ToInt32(buffer);// size of our message
    buffer = new byte[packetLen]; 
    received = socket.Receive(buffer);
    if( packetLen == received ) // we have full buffer
    {
        message = PacketHelper.UnpackProtocol(buffer);
    }
}
Console.WriteLine(message); // output: "network message 1"

答案 1 :(得分:0)

使用new byte[2000]时,您将接收到的邮件的大小限制为2KB。 我认为您可以:

  • 调整缓冲区大小,以满足消息的大小需求;和/或
  • 将您的消息拆分为多个套接字消息。

鉴于4-8K是good size for buffering socket messages,并且假设RAM大小不是问题,我将从new byte[8000]开始。

此外,您还可以发送拆分为多个块的套接字消息。也许这是个好主意。例如,如果您将msg作为要发送的消息(或对象):

private static async Task SendAnswer(Message msg, WebSocket socket)
{
    var answer = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg).ToCharArray());

    var bufferSize = 8000;
    var endOfMessage = false;
    for (var offset = 0; offset < answer.Length; offset += bufferSize)
    {
        if (offset + bufferSize >= answer.Length)
        {
            bufferSize = answer.Length - offset;
            endOfMessage = true;
        }
        await socket.SendAsync(new ArraySegment<byte>(answer, offset, bufferSize),
            WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
    }
}

在接收时,您还可以将接收分为几部分,这样就可以控制缓冲区(因此可以消耗内存)。处理完整个消息后,您应该等待来自客户端的另一条消息来做更多的事情。 Source

private async Task ReceiveMessage(WebSocket webSocket)
{
    var buffer = new byte[8000];
    var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
    while (!result.CloseStatus.HasValue)
    {
        string msg = Encoding.UTF8.GetString(new ArraySegment<byte>(buffer, 0, result.Count).ToArray());
        while (!result.EndOfMessage)
        {
            result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
            msg += Encoding.UTF8.GetString(new ArraySegment<byte>(buffer, 0, result.Count).ToArray());
        }

        //At this point, `msg` has the whole message you sent, you can do whatever you want with it.
        // [...]
        //After you handle the message, wait for another message from the client
        result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
    }
    await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}