C# Socket网络编程精华篇

摘要:
套接字作为进程间通信机制,也称为“套接字”。它用于描述IP地址和端口号,是通信链的句柄。通话结束后,一方挂断电话,这相当于关闭插座并取消连接。如果要连接到多个远程主机,则必须创建多个Socket对象。

我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念:

  1. TCP/IP层次模型

    当然这里我们只讨论重要的四层

       01,应用层(Application):应用层是个很广泛的概念,有一些基本相同的系统级TCP/IP应用以及应用协议,也有许多的企业应用和互联网应用。http协议在应用层运行。

       02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而TCP

提供传输保证。

      03,网络层(Netwok):网络层协议由一系列协议组成,包括ICMP、IGMP、RIP、OSPF、IP(v4,v6)等。

      04,链路层(Link):又称为物理数据网络接口层,负责报文传输。

   然后我们来看下tcp层次模型图

 C# Socket网络编程精华篇第1张

     从上图中可以看出,应用程序在应用层运行,在传输层,在数据前加上了TCP头,在

网络层加上的IP头,在数据链路层加上了帧。

   2,端口

    端口号范围:0-65535,总共能表示65536个数。

   按端口号可分为3大类

  (1)公认端口(WellKnownPorts):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。

  (2)注册端口(RegisteredPorts):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。

  (3)动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。

3.TCP和UDP报文

  下面一起来看下TCP和UDP的报文图

 C# Socket网络编程精华篇第2张

      从图中我们可以看出TCP和UDP中都有校验和,但是在UDP报文中,一般不使用校验和,这样可以加快数据传输的速度,但是数据的准确性可能会受到影响。换句话说,Tcp协议都有校验和,为了保证传输数据的准确性。

3.Socket

     Socket包括Ip地址和端口号两部分,程序通过Socket来通信,Socket相当于操作系统的一个组件。Socket作为进程之间通信机制,通常也称作”套接字”,用于描述IP地址和端口号,是一个通信链的句柄。说白了,就是两个程序通信用的。

生活案例对比:

      Socket之间的通信可以类比生活中打电话的案例。任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket,同时要知道对方的号码,相当于对方有一个固定的Socket,然后向对方拨号呼叫,相当于发出连接请求。假如对方在场并空闲,拿起 电话话筒,双方就可以进行通话了。双方的通话过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机,相当于关闭socket,撤销连接。

     注意:Socket不仅可以在两台电脑之间通信,还可以在同一台电脑上的两个程序间通信。

4,端口进阶(深入)

    通过IP地址确定了网络中的一台电脑后,该电脑上可能提供很多提供服务的应用,每一个应用都对应一个端口。

在Internet上有很多这样的主机,这些主机一般运行了多个服务软件 ,同时提供几种服务,每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序)

    例如:http 使用80端口,   ftp使用21端口     smtp使用25端口

5.Socket分类

     Socket主要有两种类型:

  1. 流式Socket

          是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低

     2,数据报式Socket

          是一种无连接的Socket,对应于无连接的UDP服务应用,不安全,但效率高

 6. Socket一般应用模式(服务器端和客户端)

     服务器端的Socket(至少需要两个)

        01.一个负责接收客户端连接请求(但不负责与客户端通信)

       02.每成功接收到客户端的连接便在服务器端产生一个对应的复杂通信的Socket

          021.在接收到客户端连接时创建

         022. 为每个连接成功的客户端请求在服务器端都创建一个对应的Socket(负责和客户端通信)

    客户端的Socket

  1. 必须指定要连接的服务器地址和端口
  2. 通过创建一个Socket对象来初始化一个到服务器端的TCP连接

 C# Socket网络编程精华篇第3张

      通过上图,我们可以看出,首先服务器会创建一个负责监听的socket,然后客户端通过socket连接到服务器指定端口,最后服务器端负责监听的socket,监听到客户端有连接过来了,就创建一个负责和客户端通信的socket。

下面我们来看下Socket更具体的通信过程:

Socket的通讯过程

  服务器端:

    01,申请一个socket

    02,绑定到一个IP地址和一个端口上

    03,开启侦听,等待接收连接

  客户端:

    01,申请一个socket

   02,连接服务器(指明IP地址和端口号)

   服务器端接收到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通信,原监听socket继续监听。

  注意:负责通信的Socket不能无限创建,创建的数量和操作系统有关。

 7.Socket的构造函数

    Public Socket(AddressFamily addressFamily,SocketType  socketType,ProtocolType  protocolTYpe)

    AddressFamily:指定Socket用来解析地址的寻址方案。例如:InterNetWork指示当Socket使用一个IP版本4地址连接

   SocketType:定义要打开的Socket的类型

   Socket类使用ProtocolType枚举向Windows  Sockets  API通知所请求的协议

注意:

   1,端口号必须在 1 和 65535之间,最好在1024以后。

   2,要连接的远程主机必须正在监听指定端口,也就是说你无法随意连接远程主机。

如:

IPAddress addr = IPAddress.Parse("127.0.0.1");

IPEndPoint endp = new IPEndPoint(addr,,9000);

         服务端先绑定:serverWelcomeSocket.Bind(endp)

         客户端再连接:clientSocket.Connect(endp)

   3,一个Socket一次只能连接一台主机

   4,Socket关闭后无法再次使用

  5,每个Socket对象只能与一台远程主机连接。如果你想连接到多台远程主机,你必须创建多个Socket对象。

8.Socket常用类和方法

  相关类:

   IPAddress:包含了一个IP地址

   IPEndPoint:包含了一对IP地址和端口号

 方法:

   Socket():创建一个Socket

   Bind():绑定一个本地的IP和端口号(IPEndPoint)

   Listen():让Socket侦听传入的连接吃那个病,并指定侦听队列容量

   Connect():初始化与另一个Socket的连接

   Accept():接收连接并返回一个新的Socket

   Send():输出数据到Socket

   Receive():从Socket中读取数据

   Close():关闭Socket,销毁连接

  接下来,我们同一个简单的服务器和客户端通信的案例,来看下Sokcet的具体用法,效果图如下:

 C# Socket网络编程精华篇第4张
C# Socket网络编程精华篇第5张

关键代码:

服务器端代码:

private void Form1_Load(object sender, EventArgs e)

        {

            Control.CheckForIllegalCrossThreadCalls = false;

        }

 

        private void btnListen_Click(object sender, EventArgs e)

        {

            //ip地址

            IPAddress ip = IPAddress.Parse(txtIP.Text);

           // IPAddress ip = IPAddress.Any;

            //端口号

            IPEndPoint point=new IPEndPoint(ip,int.Parse(txtPort.Text));

            //创建监听用的Socket

            /*

             * AddressFamily.InterNetWork:使用 IP4地址。

SocketType.Stream:支持可靠、双向、基于连接的字节流,而不重复数据。此类型的 Socket 与单个对方主机进行通信,并且在通信开始之前需要远程主机连接。Stream 使用传输控制协议 (Tcp) ProtocolType 和 InterNetworkAddressFamily。

ProtocolType.Tcp:使用传输控制协议。

             */

            //使用IPv4地址,流式socket方式,tcp协议传递数据

            Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

            //创建好socket后,必须告诉socket绑定的IP地址和端口号。

            //让socket监听point

            try

            {

                //socket监听哪个端口

                socket.Bind(point);

                //同一个时间点过来10个客户端,排队

                socket.Listen(10);

                ShowMsg("服务器开始监听");

                Thread thread = new Thread(AcceptInfo);

                thread.IsBackground = true;

                thread.Start(socket);

            }

            catch (Exception ex)

            {

               

               ShowMsg(ex.Message);

            }

        }

        //记录通信用的Socket

        Dictionary<string,Socket> dic=new Dictionary<string, Socket>();

       // private Socket client;

        void AcceptInfo(object o)

        {

            Socket socket = o as Socket;

            while (true)

            {

                //通信用socket

                try

                {

                    //创建通信用的Socket

                  Socket  tSocket = socket.Accept();

                  string point = tSocket.RemoteEndPoint.ToString();

                    //IPEndPoint endPoint = (IPEndPoint)client.RemoteEndPoint;

                    //string me = Dns.GetHostName();//得到本机名称

                    //MessageBox.Show(me);

                 ShowMsg(point + "连接成功!");

                 cboIpPort.Items.Add(point);

                 dic.Add(point, tSocket);

                    //接收消息

                    Thread th = new Thread(ReceiveMsg);

                    th.IsBackground = true;

                    th.Start(tSocket);

                }

                catch (Exception ex)

                {

                    ShowMsg(ex.Message);

                    break;

                }

            }

        }

        //接收消息

        void ReceiveMsg(object o)

        {

            Socket client = o as Socket;

            while (true)

            {

                //接收客户端发送过来的数据

                try

                {

                    //定义byte数组存放从客户端接收过来的数据

                    byte[] buffer = new byte[1024 * 1024];

                    //将接收过来的数据放到buffer中,并返回实际接受数据的长度

                    int n = client.Receive(buffer);

                    //将字节转换成字符串

                    string words = Encoding.UTF8.GetString(buffer, 0, n);

                  

                    ShowMsg(client.RemoteEndPoint.ToString() + ":" + words);

                }

                catch (Exception ex)

                {

                   ShowMsg(ex.Message);

                    break;

                }

            }

        }

 

        void ShowMsg(string msg)

        {

            txtLog.AppendText(msg+"
");

        }

 

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)

        {

            //主窗体关闭时关闭子线程

          

        }

        //给客户端发送消息

        private void btnSend_Click(object sender, EventArgs e)

        {

            try

            {

                ShowMsg(txtMsg.Text);

                string ip = cboIpPort.Text;

                byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);

                dic[ip].Send(buffer);

                // client.Send(buffer);

            }

            catch (Exception ex)

            {

               ShowMsg(ex.Message);

            }

 

        }

  客户端代码:

Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        private void btnConnection_Click(object sender, EventArgs e)

        {

            //连接到的目标IP

            IPAddress ip = IPAddress.Parse(txtIP.Text);

            //IPAddress ip = IPAddress.Any;

            //连接到目标IP的哪个应用(端口号!)

            IPEndPoint point=new IPEndPoint(ip,int.Parse(txtPort.Text));

            try

            {

                //连接到服务器

                client.Connect(point);

                ShowMsg("连接成功");

                ShowMsg("服务器" + client.RemoteEndPoint.ToString());

                ShowMsg("客户端:" + client.LocalEndPoint.ToString());

                //连接成功后,就可以接收服务器发送的信息了

                Thread th=new Thread(ReceiveMsg);

                th.IsBackground = true;

                th.Start();

            }

            catch (Exception ex)

            {

                ShowMsg(ex.Message);

            }

        }

        //接收服务器的消息

        void ReceiveMsg()

        {

            while (true)

            {

                try

                {

                    byte[] buffer = new byte[1024 * 1024];

                    int n = client.Receive(buffer);

                    string s = Encoding.UTF8.GetString(buffer, 0, n);

                    ShowMsg(client.RemoteEndPoint.ToString() + ":" + s);

                }

                catch (Exception ex)

                {

                    ShowMsg(ex.Message);

                    break;

                }

            }

          

        }

 

        void ShowMsg(string msg)

        {

            txtInfo.AppendText(msg+"
");

        }

 

        private void btnSend_Click(object sender, EventArgs e)

        {

            //客户端给服务器发消息

            if (client!=null)

            {

                try

                {

                   ShowMsg(txtMsg.Text);

                    byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);

                    client.Send(buffer);

                }

                catch (Exception ex)

                {

                   ShowMsg(ex.Message);

                }

            }

           

        }

 

        private void ClientForm_Load(object sender, EventArgs e)

        {

            Control.CheckForIllegalCrossThreadCalls = false;

        }

  程序员的基础教程:菜鸟程序员

免责声明:文章转载自《C# Socket网络编程精华篇》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【SQL注入】之MSSQL注入git commit 后报错:Changes not staged for commit下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

wireshark筛选器汇总

抓取指定IP地址的数据流: 如果你的抓包环境下有很多主机正在通讯,可以考虑使用所观察主机的IP地址来进行过滤。以下为IP地址抓包过滤示例: host 10.3.1.1:抓取发到/来自10.3.1.1的数据流 host 2406:da00:ff00::6b16:f02d:抓取发到/来自IPv6地址2406:da00:ff00::6b16:f02d的数据流...

谈谈网络协议 – 传输层( Transport)

传输层( Transport) 传输层有2个协议 TCP(Transmission Control Protocol),传输控制协议 UDP(User Datagram Protocol),用户数据报协议 UDP - 用户数据报协议 UDP - 首部数据格式 UDP是无连接的,减少了建立和释放连接的开销 UDP尽最大能力交付,不保证可靠交付 因...

浏览器访问网页的详细内部过程(转)

我们来看当我们在浏览器输入http://www.mytest.com:81/mytest/index.html,幕后所发生的一切。 首先http是一个应用层的协议,在这个层的协议,只是一种通讯规范,也就是因为双方要进行通讯,大家要事先约定一个规范。 1.连接 当我们输入这样一个请求时,首先要建立一个socket连接,因为socket是通过ip和端口建立的,...

打开即时通讯服务器openfire的大门

1、什么是即时通讯 你现在最常用的软件是什么,如果我没有猜错,应该是QQ和微信,是的,他们就是即时通讯软件。 一个可以让你无时无刻,只要有网络就能够沟通的工具,就是即时通讯工具。那么本教程,我们主要以openfire为基础来讲解即时通讯服务器和客户端的实现。 毫无疑问,为了更深入的理解openfire,我们也会深入openfire源码,尽可能多的了解它...

Linux网络编程笔记(修订版)

我的网络编程笔记, 因为最近又要做Linux下的网络编程,故重新修订, 其中一些内容参考了文末的链接及文章 1.   基本概念 2.   基本接口 2.1.   打开一个socket 2.2.   将socket绑定定指定的端口—bind 2.3.   侦听socket—listen (服务器端) 2.4.   等待接收请求—accept (服务器端) 2...

测试Linux下tcp最大连接数限制

现在做服务器开发不加上高并发根本没脸出门,所以为了以后吹水被别人怼“天天提高并发,你自己实现的最高并发是多少”的时候能义正言辞的怼回去,趁着元旦在家没事决定自己写个demo搞一搞。 这个测试主要是想搞明白Linux下哪些参数配置限制了连接数的最大值,上限是多少。 一、先说下demo的思路: 服务端用epoll实现,就是简简单单的接收连接,然后客户端用go的...