使用Socket通讯时总是被速度问题困扰,尤其是在同时连续多次Socket通讯时。
这里请教各位大大几点问题:
1.我的客户端可能同时向服务器多次Socket请求数据,但次数又不是很多,所以没有用异步Socket,而是使用多线程Socket。现在我是定义了一个SocketConnect类,类里方法如下:/// <summary>
        /// Socket通讯接受后转换成字符串
        /// </summary>
        /// <param name="ip">Socket通讯IP</param>
        /// <param name="sendStr">发送内容</param>
        /// <returns>通讯后终端返回字符串</returns>
        public static string SocketSendAndReceiveString(IPEndPoint ip, string sendStr)
        {
            lock(ip)
            {                Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                try
                {
                    s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000);
                    s.Connect(ip);
                    byte[] sendBy = Encoding.UTF8.GetBytes(sendStr);
                    s.Send(sendBy, sendBy.Length, 0);
                    string recvStr = "";
                    byte[] recvBy = new byte[10024];
                    int bytes = 0;
                    bytes = s.Receive(recvBy, recvBy.Length, 0);
                    recvStr += Encoding.UTF8.GetString(recvBy, 0, bytes);
                    do
                    {
                        try
                        {
                            StringReader sr = new StringReader(recvStr);
                            XmlTextReader reader = new XmlTextReader(sr);
                            DataSet ds = new DataSet();
                            ds.ReadXml(reader);
                            break;
                        }
                        catch
                        {
                            bytes = s.Receive(recvBy, recvBy.Length, 0);
                            recvStr += Encoding.UTF8.GetString(recvBy, 0, bytes);
                        }
                    } while (true);
                    s.Close();
                    return recvStr;
                }
                catch (Exception ex)
                {
                    s.Close();
                    return null;
                    //throw ex;
                }
            }
        }
代码写的很烂...呵呵,这个方法就好像是公共资源一样,每次线程Socket都调用它。为了防止死锁,我把ip锁上了。不知道这样好不好,反正是一但当前占用的线程通讯慢的话后面的就都要等了...有没有其他的方法。
2.将Socket放入线程中,怎样才能随时的终止它呢。如果线程a里的Socket出现了问题半天没有返回,我想线程a终止了另开一个线程重新通讯,但是最终线程a在后台还是抛出了异常,这个怎么弄好呀?
例如:
按钮BUTTON单击后新建线程进行Socket通讯,但其实没有连接,这样肯定连不上抛出异常,catch捕捉异常给予提示,那我要是多次单击BUTTON呢,会弹出很多提示,怎么才能之弹出一次提示。

解决方案 »

  1.   

    至少下边的while得提出去吧。下班了,明天看
    do
                        {
                            try
                            {
                                StringReader sr = new StringReader(recvStr);
                                XmlTextReader reader = new XmlTextReader(sr);
                                DataSet ds = new DataSet();
                                ds.ReadXml(reader);
                                break;
                            }
                            catch
                            {
                                bytes = s.Receive(recvBy, recvBy.Length, 0);
                                recvStr += Encoding.UTF8.GetString(recvBy, 0, bytes);
                            }
                        } while (true);
      

  2.   

    那个这个while可以不管的,这是我判断数据是否传完用的,如果数据没有传完就试图转成XML格式会出现异常然后接着接受直到成功转成XML格式为止。
      

  3.   

    Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    try
                    {
                        s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000);
                        s.Connect(ip);每次发送接受都从新和服务段连接。 把socket连接提出去,保存起来,连接一次就可以。
    另:异步方式最适合。~~~复制的代码一. Serverusing System;
    using System.Threading; // Sleeping
    using System.Net; // Used to local machine info
    using System.Net.Sockets; // Socket namespace
    using System.Collections; // Access to the Array listnamespace ChatServer
    {
    /// <summary>
    /// Main class from which all objects are created
    /// </summary>
    class AppMain
    {
    // Attributes
    private ArrayList m_aryClients = new ArrayList(); // List of Client Connections
    /// <summary>
    /// Application starts here. Create an instance of this class and use it
    /// as the main object.
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
    AppMain app = new AppMain();
    // Welcome and Start listening
    Console.WriteLine( "*** Chat Server Started {0} *** ", DateTime.Now.ToString( "G" ) ); //
    // Method 2 
    //
    const int nPortListen = 399;
    // Determine the IPAddress of this machine
    IPAddress [] aryLocalAddr = null;
    String strHostName = "";
    try
    {
    // NOTE: DNS lookups are nice and all but quite time consuming.
    strHostName = Dns.GetHostName();
    IPHostEntry ipEntry = Dns.GetHostByName( strHostName );
    aryLocalAddr = ipEntry.AddressList;
    }
    catch( Exception ex )
    {
    Console.WriteLine ("Error trying to get local address {0} ", ex.Message );
    }

    // Verify we got an IP address. Tell the user if we did
    if( aryLocalAddr == null || aryLocalAddr.Length < 1 )
    {
    Console.WriteLine( "Unable to get local address" );
    return;
    }
    Console.WriteLine( "Listening on : [{0}] {1}:{2}", strHostName, aryLocalAddr[0], nPortListen ); // Create the listener socket in this machines IP address
    Socket listener = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
    listener.Bind( new IPEndPoint( aryLocalAddr[0], 399 ) );
    //listener.Bind( new IPEndPoint( IPAddress.Loopback, 399 ) ); // For use with localhost 127.0.0.1
    listener.Listen( 10 ); // Setup a callback to be notified of connection requests
    listener.BeginAccept( new AsyncCallback( app.OnConnectRequest ), listener ); Console.WriteLine ("Press Enter to exit" );
    Console.ReadLine();
    Console.WriteLine ("OK that does it! Screw you guys I'm going home..." ); // Clean up before we go home
    listener.Close();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    }
    /// <summary>
    /// Callback used when a client requests a connection. 
    /// Accpet the connection, adding it to our list and setup to 
    /// accept more connections.
    /// </summary>
    /// <param name="ar"></param>
    public void OnConnectRequest( IAsyncResult ar )
    {
    Socket listener = (Socket)ar.AsyncState;
    NewConnection( listener.EndAccept( ar ) );
    listener.BeginAccept( new AsyncCallback( OnConnectRequest ), listener );
    } /// <summary>
    /// Add the given connection to our list of clients
    /// Note we have a new friend
    /// Send a welcome to the new client
    /// Setup a callback to recieve data
    /// </summary>
    /// <param name="sockClient">Connection to keep</param>
    //public void NewConnection( TcpListener listener )
    public void NewConnection( Socket sockClient )
    {
    // Program blocks on Accept() until a client connects.
    //SocketChatClient client = new SocketChatClient( listener.AcceptSocket() );
    SocketChatClient client = new SocketChatClient( sockClient );
    m_aryClients.Add( client );
    Console.WriteLine( "Client {0}, joined", client.Sock.RemoteEndPoint );
     
    // Get current date and time.
    DateTime now = DateTime.Now;
    String strDateLine = "Welcome " + now.ToString("G") + "\n\r"; // Convert to byte array and send.
    Byte[] byteDateLine = System.Text.Encoding.ASCII.GetBytes( strDateLine.ToCharArray() );
    client.Sock.Send( byteDateLine, byteDateLine.Length, 0 ); client.SetupRecieveCallback( this );
    } /// <summary>
    /// Get the new data and send it out to all other connections. 
    /// Note: If not data was recieved the connection has probably 
    /// died.
    /// </summary>
    /// <param name="ar"></param>
    public void OnRecievedData( IAsyncResult ar )
    {
    SocketChatClient client = (SocketChatClient)ar.AsyncState;
    byte [] aryRet = client.GetRecievedData( ar ); // If no data was recieved then the connection is probably dead
    if( aryRet.Length < 1 )
    {
    Console.WriteLine( "Client {0}, disconnected", client.Sock.RemoteEndPoint );
    client.Sock.Close();
    m_aryClients.Remove( client );      
    return;
    } // Send the recieved data to all clients (including sender for echo)
    foreach( SocketChatClient clientSend in m_aryClients )
    {
    try
    {
    clientSend.Sock.Send( aryRet );
    }
    catch
    {
    // If the send fails the close the connection
    Console.WriteLine( "Send to client {0} failed", client.Sock.RemoteEndPoint );
    clientSend.Sock.Close();
    m_aryClients.Remove( client );
    return;
    }
    }
    client.SetupRecieveCallback( this );
    }
    } /// <summary>
    /// Class holding information and buffers for the Client socket connection
    /// </summary>
    internal class SocketChatClient
    {
    private Socket m_sock; // Connection to the client
    private byte[] m_byBuff = new byte[50]; // Receive data buffer
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="sock">client socket conneciton this object represents</param>
    public SocketChatClient( Socket sock )
    {
    m_sock = sock;
    } // Readonly access
    public Socket Sock
    {
    get{ return m_sock; }
    } /// <summary>
    /// Setup the callback for recieved data and loss of conneciton
    /// </summary>
    /// <param name="app"></param>
    public void SetupRecieveCallback( AppMain app )
    {
    try
    {
    AsyncCallback recieveData = new AsyncCallback(app.OnRecievedData);
    m_sock.BeginReceive( m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, this );
    }
    catch( Exception ex )
    {
    Console.WriteLine( "Recieve callback setup failed! {0}", ex.Message );
    }
    }
      

  4.   

    /// <summary>
    /// Data has been recieved so we shall put it in an array and
    /// return it.
    /// </summary>
    /// <param name="ar"></param>
    /// <returns>Array of bytes containing the received data</returns>
    public byte [] GetRecievedData( IAsyncResult ar )
    {
                int nBytesRec = 0;
    try
    {
    nBytesRec = m_sock.EndReceive( ar );
    }
    catch{}
    byte [] byReturn = new byte[nBytesRec];
    Array.Copy( m_byBuff, byReturn, nBytesRec );

    /*
    // Check for any remaining data and display it
    // This will improve performance for large packets 
    // but adds nothing to readability and is not essential
    int nToBeRead = m_sock.Available;
    if( nToBeRead > 0 )
    {
    byte [] byData = new byte[nToBeRead];
    m_sock.Receive( byData );
    // Append byData to byReturn here
    }
    */
    return byReturn;
    }
    }
    }二.client
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Data;
    using System.Net; // Endpoint
    using System.Net.Sockets; // Socket namespace
    using System.Text; // Text encoders// Declare the delegate prototype to send data back to the form
    delegate void AddMessage( string sNewMessage );
    namespace ChatClient
    {
    /// <summary>
    /// This form connects to a Socket server and Streams data to and from it.
    /// Note: The following has been ommitted.
    /// 1) Send button need to be grayed when conneciton is 
    ///    not active
    /// 2) Send button should gray when no text in the Message box.
    /// 3) Line feeds in the recieved data should be parsed into seperate
    ///    lines in the recieved data list
    /// 4) Read startup setting from a app.config file
    /// </summary>
    public class FormMain : System.Windows.Forms.Form
    {
    // My Attributes
    private Socket m_sock; // Server connection
    private byte [] m_byBuff = new byte[256]; // Recieved data buffer
    private event AddMessage m_AddMessage; // Add Message Event handler for Form // Wizard generated code
    private System.Windows.Forms.Button m_btnConnect;
    private System.Windows.Forms.TextBox m_tbServerAddress;
    private System.Windows.Forms.ListBox m_lbRecievedData;
    private System.Windows.Forms.TextBox m_tbMessage;
    private System.Windows.Forms.Button m_btnSend;
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.Container components = null; public FormMain()
    {
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent(); // Add Message Event handler for Form decoupling from input thread
    m_AddMessage = new AddMessage( OnAddMessage );
    //
    // TODO: Add any constructor code after InitializeComponent call
    //
    } /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
    if( disposing )
    {
    if (components != null) 
    {
    components.Dispose();
    }
    }
    base.Dispose( disposing );
    } #region Windows Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
    this.m_tbServerAddress = new System.Windows.Forms.TextBox();
    this.m_tbMessage = new System.Windows.Forms.TextBox();
    this.m_btnConnect = new System.Windows.Forms.Button();
    this.m_lbRecievedData = new System.Windows.Forms.ListBox();
    this.m_btnSend = new System.Windows.Forms.Button();
    this.SuspendLayout();
    // 
    // m_tbServerAddress
    // 
    this.m_tbServerAddress.Anchor = ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
    | System.Windows.Forms.AnchorStyles.Right);
    this.m_tbServerAddress.Location = new System.Drawing.Point(8, 8);
    this.m_tbServerAddress.Name = "m_tbServerAddress";
    this.m_tbServerAddress.Size = new System.Drawing.Size(204, 20);
    this.m_tbServerAddress.TabIndex = 1;
    this.m_tbServerAddress.Text = "192.168.0.8";
    // 
    // m_tbMessage
    // 
    this.m_tbMessage.Anchor = ((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
    | System.Windows.Forms.AnchorStyles.Right);
    this.m_tbMessage.Location = new System.Drawing.Point(8, 37);
    this.m_tbMessage.Name = "m_tbMessage";
    this.m_tbMessage.Size = new System.Drawing.Size(205, 20);
    this.m_tbMessage.TabIndex = 3;
    this.m_tbMessage.Text = "";
    // 
    // m_btnConnect
    // 
    this.m_btnConnect.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right);
    this.m_btnConnect.Location = new System.Drawing.Point(228, 8);
    this.m_btnConnect.Name = "m_btnConnect";
    this.m_btnConnect.TabIndex = 0;
    this.m_btnConnect.Text = "Connect";
    this.m_btnConnect.Click += new System.EventHandler(this.m_btnConnect_Click);
    // 
    // m_lbRecievedData
    // 
    this.m_lbRecievedData.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
    | System.Windows.Forms.AnchorStyles.Left) 
    | System.Windows.Forms.AnchorStyles.Right);
    this.m_lbRecievedData.IntegralHeight = false;
    this.m_lbRecievedData.Location = new System.Drawing.Point(0, 66);
    this.m_lbRecievedData.Name = "m_lbRecievedData";
    this.m_lbRecievedData.Size = new System.Drawing.Size(311, 220);
    this.m_lbRecievedData.TabIndex = 2;
    // 
    // m_btnSend
    // 
    this.m_btnSend.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right);
    this.m_btnSend.Location = new System.Drawing.Point(228, 36);
    this.m_btnSend.Name = "m_btnSend";
    this.m_btnSend.TabIndex = 4;
    this.m_btnSend.Text = "Send";
    this.m_btnSend.Click += new System.EventHandler(this.m_btnSend_Click);
    // 
    // FormMain
    // 
    this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
    this.ClientSize = new System.Drawing.Size(312, 287);
    this.Controls.AddRange(new System.Windows.Forms.Control[] {
      this.m_btnSend,
      this.m_tbMessage,
      this.m_lbRecievedData,
      this.m_tbServerAddress,
      this.m_btnConnect});
    this.Name = "FormMain";
    this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
    this.Text = "Form1";
    this.Closing += new System.ComponentModel.CancelEventHandler(this.FormMain_Closing);
    this.ResumeLayout(false); }
    #endregion
      

  5.   


    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() 
    {
    Application.Run(new FormMain());
    } /// <summary>
    /// Connect button pressed. Attempt a connection to the server and 
    /// setup Recieved data callback
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void m_btnConnect_Click(object sender, System.EventArgs e)
    {
    Cursor cursor = Cursor.Current;
    Cursor.Current = Cursors.WaitCursor;
    try
    {
    // Close the socket if it is still open
    if( m_sock != null && m_sock.Connected )
    {
    m_sock.Shutdown( SocketShutdown.Both );
    System.Threading.Thread.Sleep( 10 );
    m_sock.Close();
    } // Create the socket object
    m_sock = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); // Define the Server address and port
    IPEndPoint epServer = new IPEndPoint(  IPAddress.Parse( m_tbServerAddress.Text ), 399 ); // Connect to the server blocking method and setup callback for recieved data
    // m_sock.Connect( epServer );
    // SetupRecieveCallback( m_sock );

    // Connect to server non-Blocking method
    m_sock.Blocking = false;
    AsyncCallback onconnect = new AsyncCallback( OnConnect );
    m_sock.BeginConnect( epServer, onconnect, m_sock );
    }
    catch( Exception ex )
    {
    MessageBox.Show( this, ex.Message, "Server Connect failed!" );
    }
    Cursor.Current = cursor;
    } public void OnConnect( IAsyncResult ar )
    {
    // Socket was the passed in object
    Socket sock = (Socket)ar.AsyncState; // Check if we were sucessfull
    try
    {
    //sock.EndConnect( ar );
    if( sock.Connected )
    SetupRecieveCallback( sock );
    else
    MessageBox.Show( this, "Unable to connect to remote machine", "Connect Failed!" );
    }
    catch( Exception ex )
    {
    MessageBox.Show( this, ex.Message, "Unusual error during Connect!" );
    }
    } /// <summary>
    /// Get the new data and send it out to all other connections. 
    /// Note: If not data was recieved the connection has probably 
    /// died.
    /// </summary>
    /// <param name="ar"></param>
    public void OnRecievedData( IAsyncResult ar )
    {
    // Socket was the passed in object
    Socket sock = (Socket)ar.AsyncState; // Check if we got any data
    try
    {
    int nBytesRec = sock.EndReceive( ar );
    if( nBytesRec > 0 )
    {
    // Wrote the data to the List
    string sRecieved = Encoding.ASCII.GetString( m_byBuff, 0, nBytesRec ); // WARNING : The following line is NOT thread safe. Invoke is
    // m_lbRecievedData.Items.Add( sRecieved );
    Invoke( m_AddMessage, new string [] { sRecieved } ); // If the connection is still usable restablish the callback
    SetupRecieveCallback( sock );
    }
    else
    {
    // If no data was recieved then the connection is probably dead
    Console.WriteLine( "Client {0}, disconnected", sock.RemoteEndPoint );
    sock.Shutdown( SocketShutdown.Both );
    sock.Close();
    }
    }
    catch( Exception ex )
    {
    MessageBox.Show( this, ex.Message, "Unusual error druing Recieve!" );
    }
    } public void OnAddMessage( string sMessage )
    {
    m_lbRecievedData.Items.Add( sMessage );
    }

    /// <summary>
    /// Setup the callback for recieved data and loss of conneciton
    /// </summary>
    public void SetupRecieveCallback( Socket sock )
    {
    try
    {
    AsyncCallback recieveData = new AsyncCallback( OnRecievedData );
    sock.BeginReceive( m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, sock );
    }
    catch( Exception ex )
    {
    MessageBox.Show( this, ex.Message, "Setup Recieve Callback failed!" );
    }
    } /// <summary>
    /// Close the Socket connection bofore going home
    /// </summary>
    private void FormMain_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
    if( m_sock != null && m_sock.Connected )
    {
    m_sock.Shutdown( SocketShutdown.Both );
    m_sock.Close();
    }
    } /// <summary>
    /// Send the Message in the Message area. Only do this if we are connected
    /// </summary>
    private void m_btnSend_Click(object sender, System.EventArgs e)
    {
    // Check we are connected
    if( m_sock == null || !m_sock.Connected )
    {
    MessageBox.Show( this, "Must be connected to Send a message" );
    return;
    } // Read the message from the text box and send it
    try
    {
    // Convert to byte array and send.
    Byte[] byteDateLine = Encoding.ASCII.GetBytes( m_tbMessage.Text.ToCharArray() );
    m_sock.Send( byteDateLine, byteDateLine.Length, 0 );
    }
    catch( Exception ex )
    {
    MessageBox.Show( this, ex.Message, "Send Message Failed!" );
    }
    }
    }
    }
      

  6.   

    public static int SendData(Socket s, byte[] data)
            {
                int total = 0;
                int size = data.Length;
                int dataleft = size;
                int sent;            while (total < size)
                {
                    sent = s.Send(data, total, dataleft, SocketFlags.None);
                    total += sent;
                    dataleft -= sent;
                }            return total;
            }        public static byte[] ReceiveData(Socket s, int size)
            {
                int total = 0;
                int dataleft = size;
                byte[] data = new byte[size];
                int recv;
                while (total < size)
                {
                    recv = s.Receive(data, total, dataleft, SocketFlags.None);
                    if (recv == 0)
                    {
                        data = null;
                        break;
                    }                total += recv;
                    dataleft -= recv;
                }
                return data;
            }
    给两条socket传输的通用方法,试试调用一下,快多了,分段传输的。
      

  7.   

    用多线程和异步调用相配合,可以达到极快,我刚做了这个,不过是VBNET的,代码可以互转换
    方法相同,原理如下:
    1.客户端
    为接收专门开个后台线程循环接收,接收到得数据,采用异步调用去显示出来,不耽误时间。
    2。服务器端
       A 先开个监听的主线程循环监听进来的新连接,此线程也开为后台线程,主要是方便关闭,同时。
       B 然后若监听线程得到一个连接后,即为此线程另开个新线程循环接收,接收到得数据,采用异步调用去显示出来,不耽误时间。 线程连接断开后,设个标志,释放此线程,以便主线程取用其资源。
       C 主线程传替连接后,继续监听循环。
       D 为每个进来的连接,建立一个接收线程,均为后台连接,主要是方便关闭,同时。
    3.你可以参看我做的VBNET程序,请参看,有意可以继续讨论。
       http://download.csdn.net/detail/chzadm/3721540
      虽然是VBNET做的,速度还是蛮快的。
      

  8.   

    我做的算是服务器端,客户端不是电脑是其他的终端设备。我要做的监控终端状态,同时呢又能向终端请求数据来查看终端的设置,比如终端下的设备列表、每个设备的信息。终端方每次只能接受一个请求,这样的话我这边有又是循环监听(也是一种请求),又要进行操作,就会出现有的请求出现死锁。这怎么解决呀,我应经lock了。