【发布时间】:2019-10-14 03:33:20
【问题描述】:
我正在尝试通过套接字连接向两台打印机发送打印指令。该脚本工作正常,然后挂起并返回报告:
System.IO.IOException:无法将数据写入传输 连接:发送或接收数据的请求被拒绝,因为 插座已经在那个方向被关闭了 之前的关机通话
或
System.Net.Sockets.SocketException (0x80004005):连接尝试失败,因为连接方没有正确响应 一段时间后,或建立连接失败,因为 连接的主机没有响应
问题是间歇性的,我无法通过附加的调试器进行重现。
谁能发现导致这种行为的原因?我遇到了线程安全问题,我认为这可能是罪魁祸首。
为代码量道歉。由于这可能归结为线程,我已经尽可能多地包含了范围。
// main entry point
class HomeController{
List<string> lsLabelResults = new List<string>("label result");
PrinterBench pbBench = new PrinterBench("192.168.2.20","192.168.2.21");
void Process(){
oPrintController = new PrintController(this);
if(GetLabel()){
// should always come out of the big printer (runs in background)
oPrintController.PrintBySocketThreaded(lsLabelResults, pbBench.PostageLabelIP);
// should always come out of the small printer
oPrintController.PrintWarningLabel();
}
}
}
class PrintController{
HomeController oHC;
private static Dictionary<string, Socket> lSocks = new Dictionary<string, Socket>();
private BackgroundWorker _backgroundWorker;
static readonly object locker = new object();
double dProgress;
bool bPrintSuccess = true;
public PrintController(HomeController oArg_HC)
{
oHC = oArg_HC;
}
public bool InitSocks()
{
// Ensure the IP's / endpoints of users printers are assigned
if (!lSocks.ContainsKey(oHC.pbBench.PostageLabelIP))
{
lSocks.Add(oHC.pbBench.PostageLabelIP, null);
}
if (!lSocks.ContainsKey(oHC.pbBench.SmallLabelIP))
{
lSocks.Add(oHC.pbBench.SmallLabelIP, null);
}
// attempt to create a connection to each socket
try
{
foreach (string sKey in lSocks.Keys.ToList())
{
if (lSocks[sKey] == null || !lSocks[sKey].Connected)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(sKey), 9100);
lSocks[sKey] = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
//lSocks[sKey].Connect(ep);
var result = lSocks[sKey].BeginConnect(ep, null, null);
bool success = result.AsyncWaitHandle.WaitOne(2000, true);
// dont need success
if (lSocks[sKey].Connected)
{
lSocks[sKey].EndConnect(result);
}
else
{
lSocks[sKey].Close();
throw new SocketException(10060); // Connection timed out.
}
}
catch(SocketException se)
{
if(se.ErrorCode == 10060)
{
oHC.WriteLog("Unable to init connection to printer. Is it plugged in?", Color.Red);
}
else
{
oHC.WriteLog("Unable to init connection to printer. Error: " + se.ErrorCode.ToString(), Color.Red);
}
}
catch (Exception e)
{
oHC.WriteLog("Unable to init connection to printer. Error: " + e.ToString(), Color.Red);
}
}
}
}catch (Exception e)
{
oHC.WriteLog(e.ToString(), true);
return false;
}
return true;
}
public bool PrintBySocketThreaded(List<string> lsToPrint, string sIP)
{
// open both the sockets
InitSocks();
bBatchPrintSuccess = false;
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.WorkerSupportsCancellation = true;
object[] parameters = new object[] { lsToPrint, sIP, lSocks };
_backgroundWorker.RunWorkerAsync(parameters);
return true;
}
// On worker thread, send to print!
public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] parameters = e.Argument as object[];
double dProgressChunks = (100 / ((List<string>)parameters[0]).Count);
int iPos = 1;
Dictionary<string, Socket> dctSocks = (Dictionary<string, Socket>)parameters[2];
foreach (string sLabel in (List<string>)parameters[0] )
{
bool bPrinted = false;
// thread lock print by socket to ensure its not accessed twice
lock (locker)
{
// get relevant socket from main thread
bPrinted = PrintBySocket(sLabel, (string)parameters[1], dctSocks[(string)parameters[1]]);
}
iPos++;
}
while (!((BackgroundWorker)sender).CancellationPending)
{
((BackgroundWorker)sender).CancelAsync();
((BackgroundWorker)sender).Dispose();
//Thread.Sleep(500);
}
return;
}
// Back on the 'UI' thread so we can update the progress bar (have access to main thread data)!
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null) MessageBox.Show(e.Error.Message);
if (bPrintSuccess) oHC.WriteLog("Printing Complete");
bBatchPrintSuccess = true;
((BackgroundWorker)sender).CancelAsync();
((BackgroundWorker)sender).Dispose();
}
/// sends to printer via socket
public bool PrintBySocket(string sArg_ToPrint, string sIP, Socket sock = null)
{
Socket sTmpSock = sock;
if (sTmpSock == null)
{
InitSocks();
if (!lSocks.ContainsKey(sIP))
{
throw new Exception("Sock not init");
}
else
{
sTmpSock = lSocks[sIP];
}
}
try
{
if(!sTmpSock.Connected || !sTmpSock.IsBound)
{
InitSocks();
if (!sTmpSock.Connected)
{
oHC.WriteLog("Unable to init connection to printer. Is it plugged in?", Color.Red);
}
}
using (NetworkStream ns = new NetworkStream(sTmpSock))
{
byte[] toSend = null;
// convert string to byte stream, or use byte stream
if (byToPrint == null)
{
toSend = Encoding.ASCII.GetBytes(sEOL + sArg_ToPrint);
}
else
{
toSend = byToPrint;
}
ns.BeginWrite(toSend, 0, toSend.Length, OnWriteComplete, null);
ns.Flush();
}
return true;
}
catch (Exception e)
{
oHC.WriteLog("Print by socket: " + e.ToString(), true);
DisposeSocks();
}
}
public bool PrintWarningLabel()
{
string sOut = sEOL + "N" + sEOL;
sOut += "A0,150,0,4,3,3,N,\"WARNING MESSAGE TO PRINT\"" + sEOL;
sOut += sEOL;
if (PrintBySocket(sOut, oHC.pbBench.SmallLabelIP))
{
oHC.WriteLog("WARNING LABEL PRINTED");
return true;
}
return false;
}
}
【问题讨论】:
-
我不知道为什么它不起作用,但这不是通常的做法。你不需要太低级别的套接字,你不需要太老派的后台工作人员,你也不需要手动线程同步。使用 TcpClient 类,
await tc.ConnectAsync(...)然后 tc.GetStream() 然后await stream.WrteAsync(...)。要将 2 个作业发送到 2 台打印机,请在上层写await Task.WhenAll(...) -
啊,听起来不错!我明天将尝试实施并报告。同时,如果有人能发现目前的情况,将不胜感激。谢谢@Sonts。我想知道我在哪里调用 DisposeSocks()。如果同时调用这两个共享套接字将被释放。
-
TcpClient 也实现了 IDisposable,
using兼容 async-await。所以你可以编写像using(var tc = new TcpClient() ){ await tc.ConnectAsync();… await stream.WriteAsync(); }这样的代码,该语言将保证客户端(以及它的套接字)在成功或异常时都会关闭。 -
感谢您的出色Soonts!明天将更新我的进展情况。遗憾的是上述解决方案不起作用。除了超时,它工作正常!
-
您的代码可以修复,这是 .NET 4.5 之前异步 I/O 的一种方式。但在现代 .NET 中,这样的 I/O 变得比您在此处所做的要简单得多,同时更可靠..
标签: c# multithreading sockets