socket 客户端收到的数据与服务器发送不一致问题
小弟在搭建一c/s架构的通信程序。现在遇到socket 客户端收到的数据与服务器发送不一致问题。客户端收到的数据总是比服务器端发送的少一点。如果在客户端程序中设置断点调试就能收全。百思不得其解,请各位高手帮助解决。
注:(所有测试都在局域网内进行)
服务器端使用ReadToEnd()方法将一文件一次性读出,然后再转换成UTF8码,再使用clientsocket.Send()方法一次传送给客户端。
客户端部分代码如下:
namespace client
{
public class client
{
static Socket serverSocket = null;
private static Socket ConnectSocket(string server, int port)
{
Socket s = null;
IPHostEntry hostEntry = null;
// Get host related information.
hostEntry = Dns.GetHostEntry(server);
// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
// an exception that occurs when the host IP Address is not compatible with the address family
// (typical in the IPv6 case).
foreach(IPAddress address in hostEntry.AddressList)
{
IPEndPoint ipe = new IPEndPoint(address, port);
Socket tempSocket =new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
tempSocket.Connect(ipe);
if(tempSocket.Connected)
{
s = tempSocket;
break;
}
else
{
continue;
}
}
return s;
}
// This method requests the home page content for the specified server.
private static string SocketSendReceive(string server, int port)
{
string request = "GET / HTTP/1.1\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n ";
Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
Byte[] bytesReceived = new Byte[256];
// Create a socket connection with the specified server and port.
Socket s = ConnectSocket(server, port);
if (s == null)
return ( "Connection failed ");
// Send request to the server.
serverSocket = s;
s.Send(bytesSent, bytesSent.Length, 0);
// Receive the server home page content.
Int32 bytes = 0;
string page = "Default HTML page on " + server + ":\r\n ";
// The following will block until te page is transmitted.
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
while(s.Available> 0)
{
bytes = s.Receive(bytesReceived,bytesReceived.Length,0);
page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
}
Console.WriteLine( "\n ");
Console.WriteLine(bytes);
return page;
}
private static void SocketSendData(Socket s)
{
String str = Console.ReadLine();
Byte[] bytesSent = Encoding.ASCII.GetBytes(str);
s.Send(bytesSent, bytesSent.Length, 0);
}
private static string SocketReceiveData(Socket s)
{
// Receive the server home page content.
Byte[] bytesReceived = new Byte[256];
Int32 bytes = 0;
string recvStr = " ";
// The following will block until te page is transmitted.
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
recvStr = recvStr + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
while (s.Available > 0)
{
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
recvStr = recvStr + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
}
Console.WriteLine( "\n ");
Console.WriteLine(bytes);
return recvStr;
}
public static void Main()
{
try
{
string host;
int port = 8001;
host = "192.168.1.212 ";
string result = SocketSendReceive(host, port);
Console.WriteLine(result);
Console.WriteLine( "\n--------------- ");
Console.WriteLine( "\n第二次发送数据\n ");
SocketSendData(serverSocket);
Console.WriteLine( "\n第二次发送完毕!\n ");
Console.WriteLine( "\n--------------- ");
Console.WriteLine( "\n第二次接收数据\n ");
Console.WriteLine(SocketReceiveData(serverSocket));
Console.WriteLine( "\n第二次接收完毕!\n ");
Console.WriteLine( "\n--------------- ");
String str = Console.ReadLine();
serverSocket.Close();
}
catch (Exception e)
{
Console.WriteLine( "Error..... " + e.StackTrace);
}
}
}
}
[解决办法]
丢失数据可能是缓冲小,里面的数据还没处理就被server发送的下一批数据覆盖了。有两个方法解决,一个是让server每交发送时间间隔大点。第二个就是在client端采用socket的异步接收处理。
个人推荐使用第二种方法,msdn中在socket里有相关的异步处理说明和例子,你可以看看。
[解决办法]
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Text;
using System.IO;
namespace SocketRun
{
public partial class _Default : System.Web.UI.Page
{
public static ManualResetEvent allDone = new ManualResetEvent(false);
protected void Page_Load(object sender, EventArgs e)
{
string host = @ "192.168.1.1 ";
int port = 3000;
#region 测试数据
//string host = @ "http://www.um114.com ";
//int port = 8000;
//Socket tempSocket = new Socket(IPAddress.Parse(host).AddressFamily,
// SocketType.Stream,
// ProtocolType.Tcp);
//tempSocket.Connect(IPAddress.Parse(host), port);
//if (tempSocket.Connected)
// this.lbInfo.Text = "Ok ";
#endregion
try
{
SocketSendReceive(host, port);
}
catch(Exception ex)
{
this.lbInfo.Text = ex.Message;
}
}
/// <summary>
/// 发起一次异步连接尝试
/// </summary>
/// <param name= "ar "> </param>
private void ConnectCallback(IAsyncResult ar)
{
allDone.Set();
Socket s = (Socket)ar.AsyncState;
s.EndConnect(ar);
}
/// <summary>
/// Asynchronous connect using the host name
/// </summary>
/// <param name= "host "> </param>
/// <param name= "port "> </param>
private Socket ConnectSocket(string host, int port)
{
Socket s = null;
#region 测试数据
//IPHostEntry hostEntry = null;
//try
//{
// hostEntry = Dns.GetHostEntry(host);
//}
//catch (Exception ex)
//{
// this.lbInfo.Text = ex.Message;
// return null;
//}
//IPAddress address = hostEntry.AddressList[0];
//IPEndPoint ipe = new IPEndPoint(address, port);
#endregion
IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(host), port);
Socket tempSocket = new Socket(ipe.Address.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
allDone.Reset();
try
{
tempSocket.BeginConnect(ipe, new AsyncCallback(ConnectCallback), tempSocket);
}
catch (Exception ex)
{
this.lbInfo.Text = ex.Message;
}
// wait here until the connect finishes.
// The callback sets allDone.
allDone.WaitOne();
if (tempSocket.Connected)
{
s = tempSocket;
}
return s;
}
/// <summary>
/// This method requests the home page content for the specified server.
/// </summary>
/// <param name= "server "> </param>
/// <param name= "port "> </param>
/// <returns> </returns>
private void SocketSendReceive(string server, int port)
{
//取得预保存的文件名
string fileName = "d:\\1.txt ";
//发送给远程主机的请求内容串
//string request = "GET / HTTP/1.1\r\nHost: " + server +
// "\r\nConnection: Close\r\n\r\n ";
string request = "kw=日本人&page=1 ";
//创建bytes字节数组以转换发送串
Byte[] bytesSent = Encoding.Default.GetBytes(request);
//声明字节数组,一次接收数据的长度为1024字节
Byte[] bytesReceived = new Byte[1024];
// Create a socket connection with the specified server and port.
Socket s = ConnectSocket(server, port);
if (s == null)
return;//( "Connection failed ");
//设置为非阻塞状态
s.Blocking = false;
// //向主机发送请求
s.Send(bytesSent, bytesSent.Length, 0);
// //声明接收返回内容的字符串
int bytes = 0;
string page = " ";
//string page = "Default HTML page on " + server + ":\r\n ";
// //循环读取,直到接收完所有数据
do
{
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
page = page + Encoding.Default.GetString(bytesReceived, 0, bytes);
}
while (bytes > 0);
//将所读取的字符串转换为字节数组
byte[] content = Encoding.ASCII.GetBytes(page);
try
{
//创建文件流对象实例
FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
//写入文件
fs.Write(content, 0, content.Length);
fs.Close();
fs.Dispose();
}
catch (Exception fe)
{
this.lbInfo.Text = "文件创建/写入错误: " + fe.Message;
}
s.Blocking = true;
//禁用Socket
s.Shutdown(SocketShutdown.Both);
//关闭Socket
s.Close();
}
}
}
[解决办法]
把数据包组包中加入结束符,然后在接收后判断。如果缓存区很小,可以接收两次后组包显示。