需求:一台服务器,三十台客户端,都处于广域网下(服务器位于公网上,客户端用3G无线)
服务器放在机房内,三十台客户端放在闹市区路边的各个点,用于展示。
客户端上展示的内容(内容为PDF格式的报纸,属于电子阅报性质)统一由服务器发送。
服务器放置服务器端程序,客户端放置客户端程序(三十个客户端放置同样的前台程序)。因为客户端设备位于闹市区路边,仅用于展示内容,所以属于无人维护性质。
所以我的设计思路是:
三十个客户端程序不间断连接服务端,如果服务端没开,则继续尝试连接。当服务端程序运行并传送文件时,客户端连接成功,则接收文件(先判断此文件是否已完整接收过(因为有可能丢包,导致接收部分),如果完整接收过则不再接收)。在测试时,先弄的一台服务端对应一台客户端,出现情况如下:
运行服务端、客户端,服务端发送文件,客户端接收,再发送,再接收,无问题。
A、此时,关闭客户端,重新打开,服务端再发送时,显示远程关闭一个现有连接对话框,关闭此对话框重新发送时,ok。再选择文件,发送,也ok。以后如果再关闭客户端,重新打开,服务端仍然会显示那个啥远程关闭连接啥的。
B、此时,关闭服务器,重新打开,服务端发送文件,会有界面阻塞状态,但文件可以发送成功。一台服务端对应两台客户端时,情况为:
服务端和客户端程序运行后,服务端发送文件,界面卡死,约过几分钟这样,报告内存溢出异常(引发类型为System.OutOfMemoryException的异常),客户端无异常信息,且接收文件完整。
再次发送时,其中一个客户端接收完全,另一个只接收到部分,且报告内存溢出。客户端无异常信息。
请教各位大牛,下附代码:
服务端:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;namespace FileServer
{
public partial class ServerFrm : Form
{
private string fileName;
private int size =66530000; string ip = "120.209.97.225";
int port = 34857; public ServerFrm()
{
InitializeComponent();
ServerFrm.CheckForIllegalCrossThreadCalls = false; //监听本地端口
tpclis = new TcpListener(IPAddress.Parse(ip), port);
tpclis.Start();
} //选择文件
private void button3_Click(object sender, EventArgs e)
{
//选择要发送的文件
DialogResult result = openPath.ShowDialog();
fileName = openPath.FileName; this.textBox1.Text = fileName; if(!string.IsNullOrEmpty(fileName))
{
this.button1.Enabled = true;
}
} //发送按钮
private void button1_Click(object sender, EventArgs e)
{ //ThreadPool.QueueUserWorkItem(new WaitCallback(onStart));
onStart(null);
//onClientAccept(null);
} void onStart(Object unused)
{
// 服务器的循环, 一直监听, 来一个, 服务一个
// 靠的是启动客户端服务线程
while (true)
{
try
{
TcpClient tcpClient = tpclis.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(new WaitCallback(onClientAccept), tcpClient);//将方法排队以便执行
}
catch
{
Thread.Sleep(100);
}
}
} /// <summary>
/// 接收文件
/// </summary>
private TcpListener tpclis=null;
private void onClientAccept(object acceptedClient)
{
TcpClient tc = null;
NetworkStream ns = null;
FileStream fsByte = null;
try
{
//获取连接上的客户端对象
//TcpClient tc = tpclis.AcceptTcpClient();
tc = acceptedClient as TcpClient;
//创建网络流
ns = tc.GetStream();
//创建一个文件流,并将打开的文件以字节流形式读入内存
fsByte = new FileStream(fileName, FileMode.Open, FileAccess.Read,FileShare.Read); //第一次发送:文件信息(文件名、文件长度),并返回此信息字节数组
byte[] a = InsertFileSign(ns, fsByte); int sCount = 0, curLen = 0;
double c = 0.0, f = (double)fsByte.Length; while (sCount < ((int)fsByte.Length) && ns.CanWrite)
{
byte[] byts = new Byte[size];
//获取读取时的实际长度
curLen = fsByte.Read(byts, 0, byts.Length);
//第二次发送:文件本身内容
ns.Write(byts, 0, curLen); //计算每次实际长度的总和
sCount = sCount + curLen;
c = sCount; //进度条:读取时实际长度/文件总长度
progressBar1.Value = Convert.ToInt32((c / f) * 100);
this.Refresh();
}
progressBar1.Value = 100;
this.Refresh();
//MessageBox.Show("文件发送成功!");
this.listBox1.Items.Add("文件发送成功!");
progressBar1.Value = 0;
}
catch (Exception ex)
{
MessageBox.Show("发送文件时异常:" + ex.Message);
}
finally
{ //关闭打开的流
fsByte.Flush();
fsByte.Close();
ns.Flush();
ns.Close();
tc.Close(); this.Refresh();
}
} /// <summary>
/// 向传输的文件写入文件结构信息标记
/// </summary>
/// <param name="ns"></param>
/// <param name="fsByte"></param>
/// <returns></returns>
private byte[] InsertFileSign(NetworkStream ns, FileStream fsByte)
{
int index = fileName.LastIndexOf('\\') + 1;
string tempName = fileName.Substring(index, fileName.Length - index);////文件名
//自定义编码方式#MName#文件名.扩展名#MLen#文件长度#End#"
string sign1 = "#MName#" + tempName + "#MLen#" + fsByte.Length.ToString() + "#End#";
byte[] a = StringTOByts(sign1);
ns.Write(a, 0, a.Length);
return a;
}
//将字符串转换为字节数组
public byte[] StringTOByts(string str)
{ //当字符向字节转换是2个字节表示一个字符
byte[] byts = new Byte[1000];
char[] chs = str.ToCharArray();
for (int index = 0; index < chs.Length; index++)
{ //取出字符的低8位并将二进制转换位十进制存入字节数组
byts[index * 2] = (byte)(chs[index] & 0xFF);
//用右移8位取出高8位并将二进制转换位十进制存入字节数组
byts[index * 2 + 1] = (byte)((chs[index] >> 8) & 0xFF);
}
//剩余空间用0填充
for (int i = (str.Length * 2); i < 1000; i++)
{
byts[i] = 0;
}
return byts;
} private void button2_Click(object sender, EventArgs e)
{
this.Close();
} }
}
服务器放在机房内,三十台客户端放在闹市区路边的各个点,用于展示。
客户端上展示的内容(内容为PDF格式的报纸,属于电子阅报性质)统一由服务器发送。
服务器放置服务器端程序,客户端放置客户端程序(三十个客户端放置同样的前台程序)。因为客户端设备位于闹市区路边,仅用于展示内容,所以属于无人维护性质。
所以我的设计思路是:
三十个客户端程序不间断连接服务端,如果服务端没开,则继续尝试连接。当服务端程序运行并传送文件时,客户端连接成功,则接收文件(先判断此文件是否已完整接收过(因为有可能丢包,导致接收部分),如果完整接收过则不再接收)。在测试时,先弄的一台服务端对应一台客户端,出现情况如下:
运行服务端、客户端,服务端发送文件,客户端接收,再发送,再接收,无问题。
A、此时,关闭客户端,重新打开,服务端再发送时,显示远程关闭一个现有连接对话框,关闭此对话框重新发送时,ok。再选择文件,发送,也ok。以后如果再关闭客户端,重新打开,服务端仍然会显示那个啥远程关闭连接啥的。
B、此时,关闭服务器,重新打开,服务端发送文件,会有界面阻塞状态,但文件可以发送成功。一台服务端对应两台客户端时,情况为:
服务端和客户端程序运行后,服务端发送文件,界面卡死,约过几分钟这样,报告内存溢出异常(引发类型为System.OutOfMemoryException的异常),客户端无异常信息,且接收文件完整。
再次发送时,其中一个客户端接收完全,另一个只接收到部分,且报告内存溢出。客户端无异常信息。
请教各位大牛,下附代码:
服务端:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;namespace FileServer
{
public partial class ServerFrm : Form
{
private string fileName;
private int size =66530000; string ip = "120.209.97.225";
int port = 34857; public ServerFrm()
{
InitializeComponent();
ServerFrm.CheckForIllegalCrossThreadCalls = false; //监听本地端口
tpclis = new TcpListener(IPAddress.Parse(ip), port);
tpclis.Start();
} //选择文件
private void button3_Click(object sender, EventArgs e)
{
//选择要发送的文件
DialogResult result = openPath.ShowDialog();
fileName = openPath.FileName; this.textBox1.Text = fileName; if(!string.IsNullOrEmpty(fileName))
{
this.button1.Enabled = true;
}
} //发送按钮
private void button1_Click(object sender, EventArgs e)
{ //ThreadPool.QueueUserWorkItem(new WaitCallback(onStart));
onStart(null);
//onClientAccept(null);
} void onStart(Object unused)
{
// 服务器的循环, 一直监听, 来一个, 服务一个
// 靠的是启动客户端服务线程
while (true)
{
try
{
TcpClient tcpClient = tpclis.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(new WaitCallback(onClientAccept), tcpClient);//将方法排队以便执行
}
catch
{
Thread.Sleep(100);
}
}
} /// <summary>
/// 接收文件
/// </summary>
private TcpListener tpclis=null;
private void onClientAccept(object acceptedClient)
{
TcpClient tc = null;
NetworkStream ns = null;
FileStream fsByte = null;
try
{
//获取连接上的客户端对象
//TcpClient tc = tpclis.AcceptTcpClient();
tc = acceptedClient as TcpClient;
//创建网络流
ns = tc.GetStream();
//创建一个文件流,并将打开的文件以字节流形式读入内存
fsByte = new FileStream(fileName, FileMode.Open, FileAccess.Read,FileShare.Read); //第一次发送:文件信息(文件名、文件长度),并返回此信息字节数组
byte[] a = InsertFileSign(ns, fsByte); int sCount = 0, curLen = 0;
double c = 0.0, f = (double)fsByte.Length; while (sCount < ((int)fsByte.Length) && ns.CanWrite)
{
byte[] byts = new Byte[size];
//获取读取时的实际长度
curLen = fsByte.Read(byts, 0, byts.Length);
//第二次发送:文件本身内容
ns.Write(byts, 0, curLen); //计算每次实际长度的总和
sCount = sCount + curLen;
c = sCount; //进度条:读取时实际长度/文件总长度
progressBar1.Value = Convert.ToInt32((c / f) * 100);
this.Refresh();
}
progressBar1.Value = 100;
this.Refresh();
//MessageBox.Show("文件发送成功!");
this.listBox1.Items.Add("文件发送成功!");
progressBar1.Value = 0;
}
catch (Exception ex)
{
MessageBox.Show("发送文件时异常:" + ex.Message);
}
finally
{ //关闭打开的流
fsByte.Flush();
fsByte.Close();
ns.Flush();
ns.Close();
tc.Close(); this.Refresh();
}
} /// <summary>
/// 向传输的文件写入文件结构信息标记
/// </summary>
/// <param name="ns"></param>
/// <param name="fsByte"></param>
/// <returns></returns>
private byte[] InsertFileSign(NetworkStream ns, FileStream fsByte)
{
int index = fileName.LastIndexOf('\\') + 1;
string tempName = fileName.Substring(index, fileName.Length - index);////文件名
//自定义编码方式#MName#文件名.扩展名#MLen#文件长度#End#"
string sign1 = "#MName#" + tempName + "#MLen#" + fsByte.Length.ToString() + "#End#";
byte[] a = StringTOByts(sign1);
ns.Write(a, 0, a.Length);
return a;
}
//将字符串转换为字节数组
public byte[] StringTOByts(string str)
{ //当字符向字节转换是2个字节表示一个字符
byte[] byts = new Byte[1000];
char[] chs = str.ToCharArray();
for (int index = 0; index < chs.Length; index++)
{ //取出字符的低8位并将二进制转换位十进制存入字节数组
byts[index * 2] = (byte)(chs[index] & 0xFF);
//用右移8位取出高8位并将二进制转换位十进制存入字节数组
byts[index * 2 + 1] = (byte)((chs[index] >> 8) & 0xFF);
}
//剩余空间用0填充
for (int i = (str.Length * 2); i < 1000; i++)
{
byts[i] = 0;
}
return byts;
} private void button2_Click(object sender, EventArgs e)
{
this.Close();
} }
}
客户端:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Net;
using System.Threading;
namespace FileServer
{
public partial class ClientFrm : Form
{
//客户端保存服务器发送的文件目录
string paths = "D://"; private int size =66530000; string ip = "120.209.97.225";
int port = 34857; public ClientFrm()
{
InitializeComponent();
//创建接收文件线程
Thread thread = new Thread(new ThreadStart(FileReceive));
Control.CheckForIllegalCrossThreadCalls = false;
thread.IsBackground = true;
thread.Start();
}
//接收文件
private void btnSendFile_Click(object sender, EventArgs e)
{
}
/// <summary>
/// 负责向服务器发送文件
/// </summary>
private void FileReceive()
{
while (true)
{
try
{
//tctClient对象可指定主机名和端口
TcpClient tc = new TcpClient(ip, port);
//创建网络流
NetworkStream ns = tc.GetStream(); //接收文件,长度、名称
int MLen;
string Mname; GetFileSignInfo(ns, out MLen, out Mname);
//检查路径如果不存在就创建它
bool fag = Directory.Exists(paths);
if (!fag)
Directory.CreateDirectory(paths); //获取已经接受过的文件名
string tempName = ReadReceivedFile();
if (Mname != tempName)
{ //创建文件
FileStream fsWriteFile = new FileStream(paths + @"\" + Mname, FileMode.OpenOrCreate);
int sCount = 0, curLen = 0;
double c = 0.0, f = MLen;
while (sCount < MLen && ns.CanRead)
{
byte[] file = new Byte[size];
curLen = ns.Read(file, 0, file.Length);
fsWriteFile.Write(file, 0, curLen);
sCount = sCount + curLen;
c = sCount;
progressBar1.Value = Convert.ToInt32((c / f) * 100);
}
progressBar1.Value = 100;
fsWriteFile.Flush();
fsWriteFile.Close();
ns.Flush();
ns.Close();
this.textBox1.Text = "文件已收到!"; //记录接收文件
WriteReceivedFile(Mname); progressBar1.Value = 0;
}
}
catch (Exception ex)
{
this.textBox1.Text = ex.Message;
continue;
}
finally { }
}
}
string path = "D:\\ReceivedFile.txt";
private string ReadReceivedFile()
{
FileStream _fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read);
StreamReader _streamReader = new StreamReader(_fileStream); string receivedname = _streamReader.ReadLine(); _streamReader.Close();
_fileStream.Close(); return receivedname;
}
private void WriteReceivedFile(string content)
{
FileStream _fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
StreamWriter _streamWriter = new StreamWriter(_fileStream);
_streamWriter.WriteLine(content);
_streamWriter.Close();
_fileStream.Close();
}
/// <summary>
/// 获得文件标记信息
/// </summary>
/// <param name="ns"></param>
/// <param name="bytes"></param>
/// <param name="MLen"></param>
/// <param name="Mname"></param>
private void GetFileSignInfo(NetworkStream ns, out int MLen, out string Mname)
{
byte[] a = new Byte[1000];
ns.Read(a, 0, 1000);
string str = BytsTOString(a, 1000);
//解析规则"#98#MName#光良 - 童话.mp3#MLen#244582#End#";
int ln1 = str.IndexOf("#MName#", 0);
int ln2 = str.IndexOf("#MLen#", ln1 + 6);
int ln3 = str.IndexOf("#End#", ln2 + 5);
//获得文件参数
MLen = int.Parse(str.Substring(ln2 + 6, ln3 - ln2 - 6));//获得歌曲长度
Mname = str.Substring(ln1 + 7, ln2 - ln1 - 7);//获得歌曲名及扩展名
}
//将字节型数据转换成字符串
public string BytsTOString(byte[] byts, int len)
{
string str = ""; char ch;
for (int index = 0; index < len / 2; index++)
{
ch = (char)(byts[index * 2] + (byts[index * 2 + 1] << 8));
str += ch;
} return str;
}
private void btnCance_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
private void button1_Click(object sender, EventArgs e)
{ //ThreadPool.QueueUserWorkItem(new WaitCallback(onStart));
onStart(null);//一对多时调用
//onClientAccept(null);//一对一时调用
}