zoukankan      html  css  js  c++  java
  • 二 C# Socket通信模式

      Socket通信有两种模式:阻塞模式和非阻塞模式。

      1:阻塞模式

      所谓阻塞模式,就是开启一个线程一直保持侦听状态(通过while(true)循环),这样该线程将一直在这个循环里运行,不会退出,因此该线程将被该循环所阻塞,是为阻塞模式。使用该模式进行通信时,必须开启一个新线程,不能将其置于主线程中,否则主线程什么事都干不了。

      阻塞模式通信中又分为两种方式——重连接和持续连接。重连接就是发送端每次发送信息时,重新与接收端进行连接;而持续连接则是发送端初始化时便与接收端进行连接,并且此后一直保持连接。这两种连接方式在编程上的区别主要体现在接收端——对于前者,接收端的Socket accept = listener.Accept()必须写在while(true)循环里面;而对于后者,则必须写在循环外面。这一点对于新手很重要,我开始接触套接字编程时,由于不知道有这么一个区别存在,所以总是不知道错误到底出现在哪里!

      下面举例说明阻塞模式下的这两种编程方式。

      1.1 阻塞模式之重连接方式

      发送端:

    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;
    using System.Net.Sockets;
    using System.Threading;

    namespace DelTest
    {
        
    public partial class Form1 : Form
        
    {
            
    public Form1()
            
    {
                InitializeComponent();
            }


            
    private void Form1_Load(object sender, EventArgs e)
            
    {}

            
            
    private void button1_Click(object sender, EventArgs e)
            
    {
                Socket _sender 
    = null;//每次发送时,都实例化一个套接字,并与客户端进行连接
                
    try
                
    {
                    _sender 
    = new Socket(
                        AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _sender.Connect(IPAddress.Parse(
    "192.168.0.53"), 2000);
                    
    if (_sender.Connected)
                    
    {
                        
    byte[] sends = Encoding.Unicode.GetBytes(this.textBox1.Text);
                        _sender.Send(sends);
                    }

                }

                
    catch (Exception ee)
                
    { }
                
    finally
                
    {
                    _sender.Close();
                }

            }

        }

    }

      

              

      

      接收端:

    using System;
    using System.Collections.Generic;
    using System.Text;

    using System.Net;
    using System.Net.Sockets;
    using System.Threading;

    namespace DelTest_Rec
    {
        
    class Program
        
    {
            
    static void Main(string[] args)
            
    {
                Test t 
    = new Test();
                t.Listen();

                Console.ReadLine();
            }

        }


        
    class Test
        
    {
            
    delegate void ThreadMethod(object obj);
            ThreadMethod _tm 
    = null;

            
    private Socket _listener = null;
            
    private string _localIP = null;

            
    public void Listen()
            
    {
                _tm 
    = Show;

                _localIP 
    = Dns.GetHostAddresses(Dns.GetHostName())[0].ToString();
                _listener 
    = new Socket(
                    AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _listener.Bind(
    new IPEndPoint(IPAddress.Parse(_localIP), 2000));
                _listener.Listen(
    50);

                Thread thread 
    = new Thread(new ThreadStart(Receive));
                thread.IsBackground 
    = true;
                thread.Start();
            }


            
    private void Receive()
            
    {
                
    try
                
    {
                    
    while (true)
                    
    {
                        Socket accept 
    = _listener.Accept();//发送端为重连接,故本语句必须放在while循环里面
                        
    byte[] rec = new byte[4999];
                                            accept.Receive(rec);
                        
    string recStr = Encoding.Unicode.GetString(rec);

                        _tm(recStr);
                    }

                }

                
    catch (Exception ee)
                
    {
                }

            }


            
    private void Show(object obj)
            
    {
                Console.WriteLine(obj.ToString());
            }

        }

    }

      1.2 阻塞模式之重连接方式

      这种模式需要注意的是:接收端应用程序必须先开启,然后才能运行发送端程序。

      发送端:

    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;
    using System.Net.Sockets;
    using System.Threading;

    namespace DelTest
    {
        
    public partial class Form1 : Form
        
    {
            
    private Socket _sender = null;
            
            
    private void Form1_Load(object sender, EventArgs e)
            
    {
                
    //程序启动时便与远程进行连接
                _sender = Connect("192.168.0.53"2000);
            }

            
            
    private void button1_Click(object sender, EventArgs e)
            
    {
                
    try
                
    {
                    
    if (_sender.Connected)
                    
    {
                        
    byte[] sends = Encoding.Unicode.GetBytes(this.textBox1.Text);
                        _sender.Send(sends);
                    }

                }

                
    catch (Exception ee)
                
    {}
            }


            
    /// <summary>
            
    /// 与远程进行连接
            
    /// </summary>
            
    /// <param name="ip">远程IP地址</param>
            
    /// <param name="port">远程侦听端口</param>
            
    /// <returns></returns>

            private Socket Connect(string ip, int port)
            
    {
                IPAddress ipa 
    = IPAddress.Parse(ip);
                IPEndPoint ipe 
    = new IPEndPoint(ipa, port);
                Socket sender 
    = new Socket(
                    AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                
    try
                
    {
                    sender.Connect(ipe);
                }

                
    catch (SocketException se)
                
    {
                    
    return null;
                }

                
    return sender;
            }


            
    public Form1()
            
    {
                InitializeComponent();
            }

        }

    }

      接收端:

    using System;
    using System.Collections.Generic;
    using System.Text;

    using System.Net;
    using System.Net.Sockets;
    using System.Threading;

    namespace DelTest_Rec
    {
        
    class Program
        
    {
            
    static void Main(string[] args)
            
    {
                Test t 
    = new Test();
                t.Listen();

                Console.ReadLine();
            }

        }


        
    class Test
        
    {
            
    delegate void ThreadMethod(object obj);
            ThreadMethod _tm 
    = null;

            
    private Socket _listener = null;
            
    private string _localIP = null;

            
    public void Listen()
            
    {
                _tm 
    = Show;

                _localIP 
    = Dns.GetHostAddresses(Dns.GetHostName())[0].ToString();
                _listener 
    = new Socket(
                    AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _listener.Bind(
    new IPEndPoint(IPAddress.Parse(_localIP), 2000));
                _listener.Listen(
    50);

                Thread thread 
    = new Thread(new ThreadStart(Receive));
                thread.IsBackground 
    = true;
                thread.Start();
            }


            
    private void Receive()
            
    {
                
    try
                
    {
                    Socket accept 
    = _listener.Accept();//发送端为持续连接,本语句必须放在while循环外面
                    while (true)
                    
    {
                        
    byte[] rec = ReceiveVarData(accept);
                        
    string recStr = Encoding.Unicode.GetString(rec);

                        _tm(recStr);
                    }

                }

                
    catch (Exception ee)
                
    {

                }

            }


            
    private void Show(object obj)
            
    {
                Console.WriteLine(obj.ToString());
            }

        }

    }

      在实际项目中,一般采用持续连接,这是因为这里的套接字所采用的传输协议是TCP/IP协议,当与远程建立连接时,需要经过三次握手,一旦出现异常,通常都会在30秒以后才能确定有没有与远程连接上,因此一般都是一次连接多次使用,而不是使用一次连接一次。一般而言,发送端需要开启一个线程专门与远程保持连接;接收端也开启一个线程专门侦听远程套接字。

      2:非阻塞模式

      所谓非阻塞模式,就是接收端不使用while循环来一直保持侦听,此时,接收端必须明确知道发送端在什么时间发送套接字,这种模式一般不会使用。下面举一例说明:

      发送端:

            private void Form1_Load(object sender, EventArgs e)
            
    {
                IPAddress ipa 
    = IPAddress.Parse(ip);
                IPEndPoint ipe 
    = new IPEndPoint(ipa, port);
                Socket sender 
    = new Socket(
                    AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                
    try
                
    {
                    sender.Connect(ipe);
                    sender.Send(Encoding.Unicode.GetBytes(
    "hello everyone!"));
                }

                
    catch (SocketException se)
                
    {}
            }

      接收端:

    using System;
    using System.Collections.Generic;
    using System.Text;

    using System.Net;
    using System.Net.Sockets;
    using System.Threading;

    namespace DelTest_Rec
    {
        
    class Program
        
    {
            
    static void Main(string[] args)
            
    {
                Test t 
    = new Test();
                t.Listen();

                Console.ReadLine();
            }

        }


        
    class Test
        
    {
            
    delegate void ThreadMethod(object obj);
            ThreadMethod _tm 
    = null;

            
    private Socket _listener = null;
            
    private string _localIP = null;

            
    public void Listen()
            
    {
                _tm 
    = Show;

                _localIP 
    = Dns.GetHostAddresses(Dns.GetHostName())[0].ToString();
                _listener 
    = new Socket(
                    AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _listener.Bind(
    new IPEndPoint(IPAddress.Parse(_localIP), 2000));
                _listener.Listen(
    50);

                            
    //非阻塞模式下,连多线程也省了,开启线程也没意义
                Socket accept = _listener.Accept();
                            
    byte[] rec = ReceiveVarData(accept);
                
    string recStr = Encoding.Unicode.GetString(rec);
                            Console.WriteLine(obj.ToString());
            }

        }

    }

      3:Socket编程中注意点

      (1)发送端和接收端所使用的编码必须一致;

      (2)注意阻塞模式中还存在如本例所说的两种套接字通信方式;

      (3)在发送端与远程建立套接字连接之前,必须运行接收端进行侦听,否则将报错;

      (4)在项目中需要引入3个空间:using System.Net;
                    
    using System.Net.Sockets;
                    
    using System.Threading;

      

      

  • 相关阅读:
    动画效果打开层 关闭层
    一个正在加载网页的进度条,加载完后,自动消失?>
    使用ASP.NET AJAX必要的配置
    css静态滤镜 + A:Hover 的效果
    可擦写的涂改文字
    各种遮罩层(lightbox)实现
    oracle 11g 安装
    SMTP协议
    javaoracle驱动包
    批量处理JDBC语句提高处理速度
  • 原文地址:https://www.cnblogs.com/pricks/p/1596115.html
Copyright © 2011-2022 走看看