путаница с сокетом безопасности - PullRequest
0 голосов
/ 16 мая 2019

У меня есть проблема безопасности потоков.

У меня есть два принтера, один загружен с маленькой этикеткой, а другой большой. Я хочу отправить 1 задание на печать через сокет на каждый принтер.

Я попытался обработать / обработать фон первого запроса (большая метка), так как это может занять много времени.

99% времени скрипт работает как положено. Маленькие и большие этикетки выходят из соответствующих принтеров.

Однако время от времени как большие, так и маленькие этикетки отправляются на один и тот же принтер! Либо как маленький, так и большой.

Я думаю, что это связано с безопасностью потоков, но мне очень трудно отследить и понять, что происходит. Я пытался добавить блокировку и закрыть сокеты после использования, но что бы я ни пытался, проблема остается.

Я пытался сократить мой код до минимума, но знаю, что этот пост все еще очень тяжелый. Любой совет будет принята с благодарностью.

// stores the printer info
class PrinterBench
{
    public PrinterBench(string sArg_PostageLabelIP, string sArg_SmallLabelIP)
    {
        PostageLabelIP = sArg_PostageLabelIP;
        SmallLabelIP = sArg_SmallLabelIP;
    }
    public string PostageLabelIP;
    public string SmallLabelIP;
}

// main entry point
class HomeController{

    PrintController oPrintController;
    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;
    public EndPoint ep { get; set; }
    public Socket sock { get; set; }
    public NetworkStream ns { get; set; }

    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
        foreach (string sKey in lSocks.Keys.ToList())
        {
            if (lSocks[sKey] == null || !lSocks[sKey].Connected )
            {
                ep = new IPEndPoint(IPAddress.Parse(sKey), 9100);
                lSocks[sKey] = new Socket(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                lSocks[sKey].Connect(ep);
            }
        }
        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];
            }
        }

        using (ns = new NetworkStream(sTmpSock))
        {
            byte[] toSend = Encoding.ASCII.GetBytes(sEOL + sArg_ToPrint);
            ns.BeginWrite(toSend, 0, toSend.Length, OnWriteComplete, null);
            ns.Flush();
        }
        return true;
    }


    public bool PrintWarningLabel()
    {
        string sOut = sEOL + "N" + sEOL;
        sOut += "LL411" + sEOL;
        sOut += "R40,0" + sEOL;
        sOut += "S5" + sEOL;
        sOut += "D15" + sEOL;
        sOut += "A0,0,0,4,4,3,N,\"!!!!!!!!!!!!!!!!!!!!!!!\"" + sEOL;
        sOut += "A0,150,0,4,3,3,N,\"WARNING MESSAGE TO PRINT\"" + sEOL;
        sOut += "A0,280,0,4,4,3,N,\"!!!!!!!!!!!!!!!!!!!!!!!\"" + sEOL;
        sOut += "P1";
        sOut += sEOL;

        if (PrintBySocket(sOut, oHC.pbBench.SmallLabelIP))
        {
            oHC.WriteLog("WARNING LABEL PRINTED");
            return true;
        }
        return false;
    }
}

1 Ответ

1 голос
/ 16 мая 2019

У вас есть это поле в PrintController:

public NetworkStream ns { get; set; }

Оно используется только здесь:

    using (ns = new NetworkStream(sTmpSock))
    {
        byte[] toSend = Encoding.ASCII.GetBytes(sEOL + sArg_ToPrint);
        ns.BeginWrite(toSend, 0, toSend.Length, OnWriteComplete, null);
        ns.Flush();
    }

Если два потока выполняют это одновременно, один можно изменитьns другому NetworkStream, когда другой собирается написать в него.

Поскольку ns используется и располагается прямо здесь, нет никаких оснований объявлять его как поле, что означает, что несколько потоков могут его перезаписать.Вместо этого удалите поле и измените свой код следующим образом:

using (var ns = new NetworkStream(sTmpSock))

Тогда несколько потоков, выполняющих это, создадут свою собственную NetworkStream как локальную переменную вместо борьбы за единицу.

IТакже проверьте все остальные поля и посмотрите, должны ли они быть полями или они могут быть просто объявлены как локальные переменные.

Непреднамеренное общее состояние плохо для многопоточного кода.Он будет вести себя точно так, как вы описали.Это работает, это работает, это работает, а затем это не работает, и воспроизвести проблему, когда вы захотите увидеть, будет почти невозможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...