同步套接字:ReceiveTimeout

Synchronous Socket: ReceiveTimeout

我有一个在读取时阻塞的套接字线程。我将套接字上的接收超时设置为 30 秒;如果没有读取到字节,则读取进入 IO 异常(内部异常:错误代码:10060)。我结束进程并关闭流并将线程设置为空。出于某种原因,我似乎无法关闭套接字连接。我不确定错误在哪里。下面是我的代码:

public class SampleClientSocket
{       
    private Socket cltSocket = null;        
    private volatile bool readyToRecv = false;        
    NetworkStream ns;
    BinaryReader br;
    BinaryWriter wr;
    private string _caller = "SampleSocketClient";
    Thread workerThread;
    
    static void Main(string[] args)
    {
        BeginWork();            
    }
    
    
    public void BeginWork()
    {            
        try
        {
            IPEndPoint ipe = new IPEndPoint("127.0.0.1", 5000);
            cltSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            cltSocket.Connect(ipe);

            cltSocket.ReceiveTimeout = 30000;
        
            cltSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);            
        
            workerThread = new Thread(doWork_01);
            workerThread.Start();                
            readyToRecv = true;
        }
        catch (Exception f)
        {
            Console.WriteLine(this._caller + f.StackTrace);
        }
    }
    
    
    
    private void doWork_01()
    {
        try
        {
            this.ns = new NetworkStream(this.cltSocket);
            this.br = new BinaryReader(this.ns);
            this.wr = new BinaryWriter(this.ns);
            readyToRecv = true;
            this._lastSocketRead = toUnixTime(DateTime.Now);
        }
        catch
        {
            Console.WriteLine(this._caller + "ERROR OPENING STREAMS.");
            readyToRecv = false;
        }
        
        while (this.readyToRecv)
        {
            
            bool _etxEncountered = false;

            MemoryStream currentMessage = new MemoryStream();
            byte buf = (byte)0;

            while (!_etxEncountered)
            {
                try
                {
                    buf = this.br.ReadByte();
                }
                catch (EndOfStreamException)
                {
                    Console.WriteLine(this._caller + "ERROR: END OF STREAM");
                    readyToRecv = false;
                    _etxEncountered = true;
                    break;
                }
                catch (ObjectDisposedException)
                {
                    Console.WriteLine(this._caller + "ERROR: Stream is closed!");
                    readyToRecv = false;
                    _etxEncountered = true;
                    break;
                }
                catch (IOException i)
                {
                    Console.WriteLine(this._caller + "ERRORSTACK: IOException!" + i.StackTrace);
                    
                    //if the ReeceiveTimeout is reached an IOException will be raised...
                    /// with an InnerException of type SocketException an ErrorCode 10060
                    var socketExpert = i.InnerException as SocketException;
                    if (socketExpert.ErrorCode != 10060)
                    {   
                        Console.WriteLine(this._caller + "ERROR: IOException!" + socketExpert.ErrorCode);
                        readyToRecv = false;
                        _etxEncountered = true;
                    }
                    else
                    {                            
                        Console.WriteLine(this._caller + "Read: IOException for timeout! NOT DISCONNECTING");
                        readyToRecv = false;
                        _etxEncountered = true;
                        break;                            
                    }
                }
                catch (Exception f)
                {
                    Console.WriteLine(this._caller + "Unknown exception" + f.StackTrace);
                    var socketExpert = f.InnerException as SocketException;
                    Console.WriteLine(this._caller + "Error code number: " + socketExpert.ErrorCode);

                    readyToRecv = false;
                    _etxEncountered = true;
                }
                if (buf.Equals(0x03) == true)
                {
                    _etxEncountered = true;
                    break;
                }
                else if (buf.Equals(0x02) == false)
                {
                    try
                    {
                        currentMessage.WriteByte(buf);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
            try { Thread.Sleep(10); }
            catch { }
        } // end while 

        EndWork();
    }
    
    public void EndWork()
    {
        try
        {
            readyToRecv = false;
            
            //close streams  
            this.br.BaseStream.Flush();
            this.br.Close();
            this.br.Dispose();
           
            this.wr.Flush();
            this.wr.Close();
            this.wr.Dispose();

            this.ns.Flush();
            this.ns.Close();
            this.ns.Dispose();

            
            this.cltSocket.Shutdown(SocketShutdown.Both);
            //this.cltSocket.Disconnect(true);
            this.cltSocket.Close();
            this.cltSocket.Dispose();
            this.cltSocket = null;                

            Console.WriteLine(this._caller + "GOODBYE...");
            try
            {
                this.workerThread.Join(1000);
                if (this.workerThread.IsAlive)
                {
                    this.workerThread.Abort();
                }
            }
            catch { }
            this.workerThread = null;
        }
        catch
        {
            this.readyToRecv = false;
            this.workerThread = null;
        }
        return;
    }
}

感谢任何帮助。

这段代码似乎有很多问题。

我认为问题的原因是您试图在工作线程上加入工作线程。 doWork_01 在工作线程上是 运行,因此 EndWork 在工作线程上也会 运行。这相当于调用 Thread.CurrentThread.Join(),并且只会死锁。此死锁可能会使您的进程保持活动状态,并可能导致诸如不释放套接字之类的问题。

我看不出有什么理由在这里尝试使用多线程。原始线程只是启动 worker,什么都不做。我建议删除所有线程,只使用套接字的局部变量,并使用 using 确保它被处理。

要正确退出进程,请使用 Environment.Exit(0);Application.Exit 或仅使用 Main 中的 return。

我有一个线程没有达到 Socket.Shutdown 和 socket.close。谢谢 jdweng 指给我看。