我正在使用套接字并将pdf,docs等小文件传输到服务器。在服务器端,我正在读取套接字数据并解析出我正在发送的文件大小。
我正在使用流,因此文件大小很长。问题是我的缓冲区是1024,所以我发送的最后一个数据包很少是1024,这意味着我最终会得到一个包含741 kbs数据的数据包,其余的缓冲区都是0。
为了解决这个问题,破坏了我的最终输出,我计算了文件大小的差异,然后创建一个新的缓冲区,只复制我需要的东西,留下垃圾。它在我的电脑上工作正常,但它会在VMware上引发算术溢出,我将要运行服务器。我认为它使用的是比我更老的.Net版本,即使我的开发PC上的目标是4.5。
这是有问题的代码。我尝试过使用check和unchecked符号,一切。有什么建议吗?
Int64 resizeTo = (int)(packet.payloadSize - packet.ms.Length);
byte[] endByte = new byte[(int)resizeTo];
Array.Copy(packet.buff, 0, endByte, 0, endByte.Length);
编辑:有问题的方法
public void ReadCallback(IAsyncResult ar)
{
try
{
PacketInfo packet = (PacketInfo)ar.AsyncState;
Socket handler = packet.socket;
int bytesRead = handler.EndReceive(ar);
string remotePoint = handler.RemoteEndPoint.ToString();
if (bytesRead > 0)
{
string peek = Encoding.UTF8.GetString( packet.buff );
if ( peek.Contains(hStart ) )
{
// file size
byte[] fSize = new byte[8];
Array.Copy(packet.buff, 8, fSize, 0, fSize.Length);
packet.payloadSize = BitConverter.ToInt64(fSize, 0);
// File Name and User ID
string[] s = peek.Split('#');
foreach (string t in s)
{
if (t.Contains(@"\"))
packet.fileName = t;
if (t.Trim().Length == 7)
packet.ADID = t;
}
}
else if ( peek.Contains(dEnd ) )
{
// Trim up last bytes of info, buffers set to1024 but if the last byte wasnt that big it gets padded with 0s by the socket
Int64 resizeTo = (int)(packet.payloadSize - packet.ms.Length);
byte[] endByte = new byte[(int)resizeTo];
Array.Copy(packet.buff, 0, endByte, 0, endByte.Length);
packet.ms.Write(endByte, 0, endByte.Length);
long payloadSize = packet.payloadSize;
long streamSize = packet.ms.Length;
// verify file size, reject if it doesn't match checksum
if ( payloadSize == streamSize )
{
// Handle completed file transfer
FileTransferCompleted(packet.ms);
packet.socket.Send(Encoding.UTF8.GetBytes(sAck));
UpdateStatus("Payload should be: " + payloadSize + " payload was: " + streamSize + " for: " + remotePoint + "\n");
UpdateStatus("Sending acknowledgement and closing socket to: " + remotePoint + "\n");
packet.socket.Close();
packet = null;
return;
}
packet.socket.Send(Encoding.UTF8.GetBytes(rAck));
packet.ms.Position = 0;
UpdateStatus("Payload should be: " + packet.payloadSize + " payload was: " + packet.ms.Length + " for: " + remotePoint + "\n");
UpdateStatus("Sending request for retransfer to: " + remotePoint + "\n");
}
else
{
packet.ms.Write(packet.buff, 0, packet.buff.Length);
//UpdateStatus("Receiving data from: " + remotePoint + "\n"); // only for debug
}
handler.BeginReceive(packet.buff, 0, packet.buff.Length,
SocketFlags.None,
new AsyncCallback(ReadCallback), packet);
//UpdateStatus("Checking for data from: " + remotePoint + "\n"); // only for debug
}
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString());
}
}
}
public class PacketInfo
{
public Socket socket = null;
public const int buffSize = 1024;
public byte[] buff = new byte[buffSize];
public MemoryStream ms = new MemoryStream();
public long payloadSize;
public string fileName = null;
public string ADID = null;
}
编辑其余的服务器代码
public class Server
{
// socket protocol messages
const string hStart = "<<head>>"; // header start
const string dEnd = "<<end>>"; // data end
const string sAck = "<<success>>"; // received
const string rAck = "<<resend>>"; // received, didn't match checksum, resend
int _port;
int _maxConnections;
// Internal event
ManualResetEvent allDone = new ManualResetEvent(false);
// Public update event for status updates
public delegate void StatusUpdateHandler(object sender, StatusUpdateEvent e);
public event StatusUpdateHandler onStatusUpdate;
// public event for file completion status
public delegate void FileTransferHandler(object sender, FileTransferComplete e);
public event FileTransferHandler onFileTransferComplete;
public int Port {
get { return _port; }
}
public int MaxConnections {
get { return _maxConnections; }
}
public Server( int port, int maxConnections )
{
this._port = port;
this._maxConnections = maxConnections;
}
void UpdateStatus(string status)
{
if (onStatusUpdate == null)
{
Debug.WriteLine("NULL");
return;
}
StatusUpdateEvent args = new StatusUpdateEvent(status);
onStatusUpdate(this, args);
}
void FileTransferCompleted( object payload )
{
if ( onFileTransferComplete == null )
{
Debug.WriteLine("Null File Transfer Completed Event");
return;
}
FileTransferComplete args = new FileTransferComplete(payload);
onFileTransferComplete(this, args);
}
public void SpinUp()
{
IPHostEntry ipHost = Dns.GetHostEntry("");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, _port);
SocketPermission permission = new SocketPermission(
NetworkAccess.Accept,
TransportType.Tcp,
"",
SocketPermission.AllPorts
);
permission.Demand();
Socket sListener = new Socket(
ipAddr.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp
);
sListener.Bind(ipEndPoint);
sListener.Listen(_maxConnections);
while (true)
{
allDone.Reset();
UpdateStatus("Waiting for connections on port " + _port + ":\n");
Debug.WriteLine("Waiting for connections.");
sListener.BeginAccept(
new AsyncCallback(AcceptCallback),
sListener );
allDone.WaitOne();
}
}
public void AcceptCallback(IAsyncResult ar)
{
allDone.Set();
try
{
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
UpdateStatus("Connection received from: " + handler.RemoteEndPoint + "\n" );
handler .NoDelay = false;
PacketInfo packet = new PacketInfo();
packet.socket = handler;
handler .BeginReceive(
packet.buff,
0,
packet.buff.Length,
SocketFlags.None,
new AsyncCallback(ReadCallback),
packet
);
}
catch ( OverflowException overflow )
{
MessageBox.Show(overflow.ToString());
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString());
}
}
客户端代码
namespace Client
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
const string hStart = "<<head>>"; // header start
const string dEnd = "<<end>>"; // data end
const string sAck = "<<success>>"; // received
const string rAck = "<<resend>>"; // resend
BindingList<string> connections = new BindingList<string>();
public List<byte[]> buildTransfer( FileStream fs )
{
List<byte[]> packets = new List<byte[]>();
using ( fs )
{
using ( MemoryStream ms = new MemoryStream() )
{
fs.CopyTo(ms);
ms.Position = 0;
ms.Flush();
byte[] header = genHeader( ms.Length, fs.Name, Environment.UserName );
packets.Add(header);
int incomingOffset = 0;
while(incomingOffset < ms.ToArray().Length)
{
byte[] buffer = new byte[1024];
int length =
Math.Min(buffer.Length, ms.ToArray().Length - incomingOffset);
if ( length < buffer.Length )
{
buffer = new byte[length + dEnd.Length];
byte[] endblock = Encoding.UTF8.GetBytes(dEnd);
Buffer.BlockCopy(ms.ToArray(), incomingOffset,
buffer, 0,
length);
Buffer.BlockCopy(endblock, 0, buffer, length, endblock.Length);
packets.Add(buffer);
return packets;
}
Buffer.BlockCopy(ms.ToArray(), incomingOffset,
buffer, 0,
length);
incomingOffset += length;
packets.Add(buffer);
}
}
}
byte[] footer = new byte[1024];
footer[0] = Encoding.UTF8.GetBytes(dEnd)[0];
packets.Add(footer);
return packets;
}
byte[] genHeader( long fileSize, string fileName, string ADID )
{
byte[] header = new byte[1024];
byte[] adid = Encoding.UTF8.GetBytes(Environment.UserName);
byte[] fName = Encoding.UTF8.GetBytes(fileName);
byte[] start = Encoding.UTF8.GetBytes(hStart);
byte pad = Encoding.UTF8.GetBytes("#")[0];
int x = 0;
foreach( byte b in start )
{
header[x] = b;
x++;
}
foreach( byte b in BitConverter.GetBytes(fileSize) )
{
header[x] = b;
x++;
}
header[x] = pad;
x++;
foreach( byte b in fName )
{
header[x] = b;
x++;
}
header[x] = pad;
x++;
foreach( byte b in adid )
{
header[x] = b;
x++;
}
header[x] = pad;
x++;
return header;
}
public MainForm()
{
InitializeComponent();
dataGridView1.DataSource = connections;
for( int x = 0; x < 1; x++ )
{
Thread nt = new Thread(connect);
nt.Start();
Thread.Sleep(1000);
}
//connect();
}
void connect()
{
byte[] bytes = new byte[1024];
Socket senderSock;
try
{
// Create one SocketPermission for socket access restrictions
SocketPermission permission = new SocketPermission(
NetworkAccess.Connect, // Connection permission
TransportType.Tcp, // Defines transport types
"", // Gets the IP addresses
SocketPermission.AllPorts // All ports
);
// Ensures the code to have permission to access a Socket
permission.Demand();
// Resolves a host name to an IPHostEntry instance
IPHostEntry ipHost = Dns.GetHostEntry("");
// Gets first IP address associated with a localhost
IPAddress ipAddr = ipHost.AddressList[0];
// Creates a network endpoint
IPEndPoint ipEndPoint = CreateIPEndPoint("10.212.98.71:4510");
//IPEndPoint ipEndPoint = new IPEndPoint(ipHost.AddressList[0], 4510);
// Create one Socket object to setup Tcp connection
senderSock = new Socket(
ipAddr.AddressFamily,// Specifies the addressing scheme
SocketType.Stream, // The type of socket
ProtocolType.Tcp // Specifies the protocols
);
senderSock.NoDelay = false; // Using the Nagle algorithm
// Establishes a connection to a remote host
senderSock.Connect(ipEndPoint);
connections.Add(senderSock.LocalEndPoint.ToString());
List<byte[]> toSend = new List<byte[]>();
using( FileStream fs = new FileStream(@"C:\Users\jdy5tnh\Downloads\b3.pdf", FileMode.Open) )
{
toSend = buildTransfer(fs);
}
while( true )
{
foreach( byte[] b in toSend )
senderSock.Send(b);
byte[] buffer = new byte[1024];
senderSock.Receive(buffer);
if (Encoding.UTF8.GetString(buffer).Contains(sAck ) )
{
senderSock.Close();
senderSock.Dispose();
return;
}
else
{
MessageBox.Show(Encoding.UTF8.GetString(buffer));
}
}
} catch (Exception exc) {
MessageBox.Show(exc.ToString());
}
}
public static IPEndPoint CreateIPEndPoint(string endPoint)
{
string[] ep = endPoint.Split(':');
if (ep.Length != 2)
throw new FormatException("Invalid endpoint format");
IPAddress ip;
if (!IPAddress.TryParse(ep[0], out ip)) {
throw new FormatException("Invalid ip-adress");
}
int port;
if (!int.TryParse(ep[1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port)) {
throw new FormatException("Invalid port");
}
return new IPEndPoint(ip, port);
}
}
}
答案 0 :(得分:1)
改变了我的方法并解决了问题,但它突出了另一个,因为本地我在同一台PC上创建客户端和服务器,所以我的应用程序工作正常。当我在网络中的另一台PC上使用它时,它会改变我的套接字传输的大小,因此我的代码停止工作。每次我将文件发送到服务器时,传输的大小都会增加。
首先运行它的功能很好,第二次运行它增加了大约700字节,第三次运行它再次增加,等等。所以我想像上面提到的我在我的方法中有一个根本问题。服务器部分几乎是从MSDN复制的,但客户端我只是把它放在一起所以它可能是问题的根源。
谢谢大家。
int result = peek.IndexOf(dEnd, StringComparison.Ordinal);
byte[] endByte = new byte[result];
答案 1 :(得分:1)
看起来您假设每次阅读时,您都会读取正好1,024个字节(或最后一个短块)。这可能不是这样的。如果您在致电bytesRead
后查看EndRead
值,我认为您会发现每次阅读时您收到的字节数差异很大。
这是基于我能看到的代码。你已经遗漏了很多,尤其是对BeginRead
的号召,没有这个号召,我就无法告诉你他在做什么。但根据您发布的内容和您对问题的描述,上述内容似乎是主要问题。
您需要拆分代码,使其汇编数据包,然后对其进行解析。您需要继续读取并向临时缓冲区添加字节,直到您收到该数据包的所有字节为止。然后你可以解析数据包并做你需要做的事情。
我前一段时间写了一个小编。请参阅Reading data from streams,以及Part 2和Part 3。