我是靠谱客的博主 神勇秋天,最近开发中收集的这篇文章主要介绍C #应用SOCKET实现TCP/IP协议的通讯C#学习TCP/IP之SOCKET通信应用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

C#学习TCP/IP之SOCKET通信应用

基本知识

OSI七层网络模型与TCP/IP四层网络模型

OSI七层网络模型与TCP/IP四层网络模型

由上图可以看出OSI参考模型网络由下往上分为:
物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

七层网络模型
图1-1 网络模型

而TCP/IP通讯协议采用了4层的层级结构,IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层。socket则是对TCP/IP协议的封装和应用。
1.链路层(数据链路层/网络接口层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡

2.网络层(互联网层):处理分组在网络中的活动,比如分组的选路。

3.运输层:主要为两台主机上的应用提供端到端的通信。

4.应用层:负责处理特定的应用程序细节。

网络层与运输层的区别:

  在TCP/TP协议族中,

  网络层IP提供的是一种不可靠的服务。它只是尽可能快地把分组从源节点送到目的节点,但不提供任何可靠性的保证。

  Tcp在不可靠的ip层上,提供了一个可靠的运输层,为了提供这种可靠的服务,TCP采用了超时重传、发送和接受端到端的确认分组等机制。

TCP/IP网络通信

  以下简单介绍TCP/IP中的协议都具备什么样的功能,都是如何工作的:
 1. IP 网际协议IP是TCP/IP的心脏,也是网络层中最重要的协议。

  IP层接收由更低层(网络接口层例如以太网设备驱动程序)发来的数据包,并把该数据包发送到更高层—TCP或UDP层;相反,IP层也把从TCP或 UDP层接收来的数据包传送到更低层。IP数据包是不可靠的,因为IP并没有做任何事情来确认数据包是按顺序发送的或者没有被破坏。IP数据包中含有发送它的主机的地址(源地址)和接收它的主机的地址(目的地址)。

  高层的TCP和UDP服务在接收数据包时,通常假设包中的源地址是有效的。也可以这样说,IP地址形成了许多服务的认证基础,这些服务相信数据包是从一个有效的主机发送来的。IP确认包含一个选项,叫作IP source routing,可以用来指定一条源地址和目的地址之间的直接路径。对于一些TCP和UDP的服务来说,使用了该选项的IP包好象是从路径上的最后一个系统传递过来的,而不是来自于它的真实地点。这个选项是为了测试而存在的,说明了它可以被用来欺骗系统来进行平常是被禁止的连接。那么,许多依靠IP源地址做确认的服务将产生问题并且会被非法入侵。

  2. TCP

  如果IP数据包中有已经封好的TCP数据包,那么IP将把它们向‘上’传送到TCP层。TCP将包排序并进行错误检查,同时实现虚电路间的连接。TCP数据包中包括序号和确认,所以未按照顺序收到的包可以被排序,而损坏的包可以被重传。

  TCP将它的信息送到更高层的应用程序,例如Telnet的服务程序和客户程序。应用程序轮流将信息送回TCP层,TCP层便将它们向下传送到IP层,设备驱动程序和物理介质,最后到接收方。

  面向连接的服务(例如Telnet、FTP、rlogin、X Windows和SMTP)需要高度的可靠性,所以它们使用了TCP。DNS在某些情况下使用TCP(发送和接收域名数据库),但使用UDP传送有关单个主机的信息。

服务器端的建立和客户端的建立

一、TCP连接的三次握手
   第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
   第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
  握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。
  理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)。

TCP连接的三次握手
图1-2 TCP连接的三次握手

二、利用Socket建立网络连接的步骤
  建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
  1、服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
  2、客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
  3、连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给 客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

  因此,TCP/IP下建立连接首先要有一个服务器,它是被动的,它只能等待别人跟它建立连接,自己不会去主动连接,而客户端则需要根据IP地址和Port端口去连接,一个服务器可以接受多个客户端的连接,但是一个客户端只能连接一台服务器,在连接后,服务器自动划分内存区域以分配各个客户端的通讯。
服务器端的建立和客户端的建立,如图所示:

服务器端的建立
图1-3 服务端的建立

客户端的建立
图1-4 客户端的建立

C#中SOCKET通信应用

套接字(socket)概念

  套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
  

服务器端

  1. 服务端创建一个socket对象
    参数 :寻址方案,ip版本4 ;套接字类型,字节流 ;协议,TCP
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  2. 在外边创建new一个空的socket对象比如serverSocket.来保存上面创建的对象。一个服务器端可以接受N个客户端的连接,因此,在服务器端,有必要对每个连接上来的客户端进行登记,创建一个List<Socket>list来保存连客户端socket对象。
  3. socket绑定:socket.Bind(new IPEndPoint(IPAddress.Parse(txtIP.Text.ToString()), int.Parse(txtPort.Text)));
  4. 设置连接等待队列的数量 socket.Listen(10);
  5. 开始服务,等待客户端连接,
  6. 登记客户端 ClientList.Add(proxSocket);
    开始接收新的消息 ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveData), proxSocket);
  7. 读取接收数据,判断连通性readLen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);
  8. 关闭socket proxSocket.Close(100);
private static byte[] result = new byte[1024];
private static int myProt = 8888 ;   //端口    
static Socket serverSocket;
List<Socket> ClientList = new List<Socket>(); //客户端列表
private bool state = false;

private void button1_Click(object sender, EventArgs e)
   {
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

     if (!state)
      {
        serverSocket = socket;
        //ip port  
        socket.Bind(new IPEndPoint(
        IPAddress.Parse(txt_IPAddress.Text.ToString()),int.Parse(txt_Port.Text)));
        //listen  
        socket.Listen(10);//连接等待队列  
        ThreadPool.QueueUserWorkItem(new WaitCallback(this.AcceptClientConnect), socket);  

        state = true;  
        button1.Text = "关闭服务";
       }
      else
      {
         try
         {
             serverSocket.Close();
             serverSocket.Shutdown(SocketShutdown.Both);  
         }
         catch (Exception )
         { 
         } 
            state =false ;
            serverSocket.Close();
            button1.Text = "开启服务"; 
         }

        }
        //日志文本框追加数据  
        public void AppendTextToTxtLog(string txt)
        {
            if (lb_Log.InvokeRequired)
            {
                lb_Log.BeginInvoke(new Action<string>(s =>
                {
                    this.lb_Log.Text = string.Format("{0}rn{1}", s, lb_Log.Text);
                }), txt);
            }
            else
            {
                this.lb_Log.Text = string.Format("{0}rn{1}", txt, lb_Log.Text);
            }
        }

        private void AcceptClientConnect(object socket)
        {
            var serverSocket = socket as Socket;

            this.AppendTextToTxtLog("服务器端开始接受客户端的链接");
            while (true)
            {
              try
              {
                var proxSocket = serverSocket.Accept();

                this.AppendTextToTxtLog(string.Format(
                "客户端{0}连接上了",proxSocket.RemoteEndPoint.ToString()));
                ClientList.Add(proxSocket);

                //接受消息  
                ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveData), proxSocket);
              }
              catch (Exception)
              {
              }
            }  
        }
public void ReceiveData(object obj)
  {
    Socket proxSocket = obj as Socket;
    byte[] data = new byte[1024 * 1024];
    while (true)
    {
       int readLen = 0;
       try
       {
         readLen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);
       }
       catch (Exception ex)
       {
         //异常退出时  
         AppendTextToTxtLog(string.Format(
         "客户端{0}非正常退出", proxSocket.RemoteEndPoint.ToString()));
         ClientList.Remove(proxSocket);
         StopConnetct(proxSocket);
         return;
       }
       if (readLen <= 0)
       {
           //客户端正常退出  
           AppendTextToTxtLog(string.Format(
           "客户端{0}正常退出", proxSocket.RemoteEndPoint.ToString()));
           ClientList.Remove(proxSocket);
           StopConnetct(proxSocket);
           return;//方法结束->终结当前接受客户端数据的异步线程  
       }
        string txt = Encoding.Default.GetString(data, 0, readLen);
        AppendTextToTxtLog(string.Format(
        "接收到客户端{0}的消息{1}", proxSocket.RemoteEndPoint.ToString(), txt));
    }
 }
private void StopConnetct(Socket proxSocket)
    {
       try
       {
           if (proxSocket.Connected)
           {
               proxSocket.Shutdown(SocketShutdown.Both);
               proxSocket.Close(100);
           }
       }
       catch (Exception )
       { 
       }
    }

private void btn_Send_Click(object sender, EventArgs e)
    {
        foreach (Socket s in this.ClientList)
        {   //服务端广播式发送给客户端
            (s as Socket).Send(Encoding.UTF8.GetBytes(txtLog.Text));
        } 
    }

客户端

  1. 要创建一个客户端socket对象名称为ClientSocket.(命名空间在System.Net.Sockets),接着确定连接类型等,调用对象下的方法 socket.Connect(txt_IP.Text.ToString(), int.Parse(txt_Port.Text.ToString()));调用完毕这个函数,系统将进行尝试连接服务器。
  2. 连接成功后,启动数据侦听, Thread thread = new Thread(new ParameterizedThreadStart(ReceiveData));
  3. 根据收到的数据判断连接正常还是连接异常 readlen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);如果获取的readlen为0表示客户端已经断开连接。
      
private bool state = false;
public Socket ClientSocket;

private void btn_OpenClose_Click(object sender, EventArgs e)
  {
      if (!state)
      {
          Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
          ClientSocket = socket;
          try
          {
              socket.Connect(txt_IP.Text.ToString(), int.Parse(txt_Port.Text.ToString()));
          }
          catch (Exception)
          {
              MessageBox.Show("连接失败");
              return;
          }
          state = true;
          btn_OpenClose.Text = "客户端已连接";
          Thread thread = new Thread(new ParameterizedThreadStart(ReceiveData));
          thread.IsBackground = true;
          thread.Start(ClientSocket);

      }
      else
      {
          try
          {
              state = false ;
              btn_OpenClose.Text = "客户端已断开";
              StopConnetct();
          }
          catch (Exception)
          {

          }

      }
  }

private void ReceiveData(object obj)
 {
     Socket proxSocket = obj as Socket;
     byte[] data = new byte[1024 * 1024];
     while (true)
     {
         int readlen = 0;
         try
         {
             readlen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None);
         }
         catch (Exception)
         {
             //异常退出时  
             StopConnetct();
             return;
         }
         if (readlen <= 0)
         {
             //客户端正常退出  
             StopConnetct();
             return;//方法结束->终结当前接受客户端数据的异步线程  
         }

       string strMsg = ProcessRecieveString(data, readlen);
       AppendTextToTxtLog(string.Format(
       "接收到服务端{0}的消息{1}",proxSocket.RemoteEndPoint.ToString(), strMsg));
     }
 }
public void AppendTextToTxtLog(string txt)
 {
     if (this.txt_rec.InvokeRequired)
     {
         this.txt_rec.BeginInvoke(new Action<string>(s => { this.txt_rec.Text = String.Format("{0}rn{1}", s, txt_rec.Text); }), txt);
     }
     else
     {
         this.txt_rec.Text = string.Format("{0}rn{1}", txt, txt_rec.Text);
     }
 } 
public string ProcessRecieveString(byte[] data, int readLen)
 {
     string str = Encoding.Default.GetString(data, 0, readLen);
     return str;
 }

private void StopConnetct()
 {
     try
     {
         if (ClientSocket.Connected)
         {
             ClientSocket.Shutdown(SocketShutdown.Both);
             ClientSocket.Close(100);
         }
     }
     catch (Exception ex)
     {
     }
 }  
private void btn_Send_Click(object sender, EventArgs e)
 {

     if (ClientSocket == null)
     {
         return;
     }
     if (ClientSocket.Connected)
     {
         byte[] data = Encoding.Default.GetBytes(txt_sendSMG.Text);
         ClientSocket.Send(data, 0, data.Length, SocketFlags.None);
     }  
 }  

注 :
f (this.InvokeRequired)
{
this.BeginInvoke(new MethodInvoker(LoadGlobalImage));
return;
}
是什么意思
答: c#中禁止跨线程直接访问控件,InvokeRequired是为了解决这个问题而产生的
当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它,此时它将会在内部调用new MethodInvoker(LoadGlobalImage)来完成下面的步骤,这个做法保证了控件的安全。

测试效果

简单的测试,
服务端发送数据采用的是广播式发送,就是在开启多个客户端情况下,服务器发送的数据客户端会同时收到

这里写图片描述
图 1-5 测试效果图

参考地址:

C# Socket TCP 编程,客户端与服务端连接,发送字符串,文件
C#中Socket通信编程的同步实现
C# socket通讯

最后

以上就是神勇秋天为你收集整理的C #应用SOCKET实现TCP/IP协议的通讯C#学习TCP/IP之SOCKET通信应用的全部内容,希望文章能够帮你解决C #应用SOCKET实现TCP/IP协议的通讯C#学习TCP/IP之SOCKET通信应用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部