socket编程基础知识

IP地址

为了使网络上的计算机能够彼此识别对方,每台计算机都需要一个IP地址以标识自己。IP地址由IP协议规定,以32位的二进制数示。最新的IPv6协议将IP地址升为128位,这使得IP地址更加广泛,能够很好地解决目前IP地址紧缺的情况,但是IPv6协议距离实际应用还有一段距离,目前多数操作系统和应用软件都是以32位的IP地址为基准。32位的IP地址主要分为两部分,即前缀和后缀。前缀表示计算机所属的物理网络,后缀确定该网络上的唯一一台计算机。在互联网中,每一个物理网络都有一个唯一的网络号。根据网络号的不同,可以将IP地址分为5类,即A类、B类、C类、D类和E类。其中,A类、B类和C类属于基本类;D类用于多播发送;E类属于保留。

在上述IP地址中,有几个I P地址是比较特殊的,有其单独的用途。
 网络地址:在IP地址中主机地址为0的表示网络地址。例如,128.111.0.0。
 广播地址:在网络号后跟所有位全是1的IP地址,表示广播地址。
 回送地址:127.0.0.1表示回送地址,用于测试。

域名

虽然使用IP地址可以标识网络中的计算机,但是IP地址容易混淆,并且不容易记忆,人们更倾向于使用主机名来标识IP地址。由于在Internet上存在许许多多的计算机,为了防止主机名相同, Internet管理机构采取了在主机名后加上后缀名的方法标识一台主机,其后缀名被称为域名。例如,www.mingrisoft.com,主机名为www,域名为mingrisoft.com。这里的域名为2级域名, com为一级域名,表示商业组织;mingrisoft为二级域名,表示本地名。为了能够利用域名进行不同主机间的通信,需要将域名解析为IP地址,称之为域名解析。域名解析是通过域名服务器来完成的。假如主机A的本地域名服务器是dns.local.com,根域名服务器是dns.mr.com;所要访问的主机B的域名为www.mingribook.com,域名服务器为dns.mrbook.com。当主机A通过域名www.mingribook.com访问主机B时,将发送解析域名www.mingribook.com的报文;本地的域名服务器收到请求后,查询本地缓存,假设没有该记录,则本地域名服务器dns.local.com向根域名服务器dns.mr.com发出解析域名www.mingribook.com的请求;根域名服务器dns.mr.com收到请求后查询本地记录,如果发现“mingribook.com NS dns.mrbook.com”信息,将给出dns.mrbook.com的IP地址,并将结果返回给主机A的本地域名服务器dns.local.com;当本地域名服务器dns.local.com收到信息后,会向主机B的域名服务器dns.mrbook.com发送解析域名www.mingribook.com的报文;当域名服务器dns.mrbook.com收到请求后,开始查询本地的记录, 发现“ www.mingribook.com A 211.120.X.X ” 其 中 “ 211.120.X.X ” 表示域名www.mingribook.com的IP地址,类似的信息,将结果返回给主机A的本地域名服务器dns.local.com。

地址解析

所谓地址解析是指将计算机的协议地址解析为物理地址,即MAC地址,又称为媒体设备地址。通常,在网络上由地址解析协议ARP来实现地址解析。下面以本地网络上的两台计算机通信为例介绍ARP协议解析地址的过程。
假设主机A和主机B处于同一个物理网络上,主机A的IP为192.168.1.21,主机B的IP为192.168.1.23,当主机A与主机B进行通信时,主机B的IP地址192.168.1.23将按如下步骤被解析为物理地址。
(1)主机A从本地ARP缓存中查找IP为192.168.1.23对应的物理地址。用户可以在命令窗口中输入“arp -a”命令查看ARP缓存
(2)如果主机A在ARP缓存中没有发现192.168.1.23映射的物理地址,将发送ARP请求帧到本地网络上的所有主机,在ARP请求帧           中包含了主机A的物理地址和IP地址。
(3)本地网络上的其他主机接收到ARP请求帧后,将检查是否与自己的IP地址匹配。如果不匹配,则丢弃ARP请求帧。如果主机B           发现与自己的IP地址匹配,则将主机A的物理地址和IP地址添加到自己的ARP缓存中,然后主机B将自己的物理地址和IP地址发         送到主机A,当主机A接收到主机B发来的信息时,将以这些信息更新ARP缓存。
(4)当主机B的物理地址确定后,主机A就可以与主机B通信了。


端口

在网络上,计算机是通过IP地址来彼此标识自己的,但是当涉及两台计算机之间的具体通信时,还会出现一个问题——如果主机A中的应用程序A1想与主机B中的应用程序B1通信,如何知道主机A中是A1应用程序与主机B中的应用程序通信,而不是主机A中的其他应用程序与主机B中的应用程序通信?反之,当主机B接收到数据时,又如何知道数据是发往应用程序B1的呢(因为在主机B中可以同时运行多个应用程序)?

为了解决上述问题,TCP/IP协议提出了端口的概念,用于标识通信的应用程序。当应用程序(严格说应该是进程)与某个端口绑定后,系统会将收到的给该端口的数据送往该应用程序。端口是用一个16位的无符号整数值来表示的,范围为0~65535。低于256的端口被作为系统的保留端口,用于系统进程的通信;不在这一范围的端口号被称为自由端口,可以由进程自由使用。


server端

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;



namespace socketserver
{
    class Program
    {
        static void Main(string[] args)
        {
            Socket client;//用这个socket与请求连接的客户端通信
            //int recv;
            //byte[] message = new byte[1024];
            Socket ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            IPAddress adr = IPAddress.Parse("127.0.0.1");
            IPEndPoint ep = new IPEndPoint(adr, 3000);

            ServerSocket.Bind(ep); //类似于客户机的connect()方法
            ServerSocket.Listen(3); //开始监听

            while (true)
            {
                if ((client = ServerSocket.Accept()) != null)
                {
                    Console.WriteLine("连接上...发送数据....");
                    //欲发送的字节数组,以0为结束标记 
                    
                    byte[] msg = Encoding.UTF8.GetBytes("This is a test");
                    byte[] message = { 10, 20, 30, 40, 50, 60, 0 };
                    //send方法返回发送的字节数
                    Console.WriteLine("总计将发送" + client.Send(msg) + "个字节的数据");
                    Console.WriteLine("结束.");
                   // Console.ReadLine();
                    client.Close();
                    break;
                } 
            }
        }
    }
}

client端

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;


namespace socketclient
{
    class Program
    {
        static void Main(string[] args)
        {
            Socket Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); //建立客户端套接字
            IPAddress adr = IPAddress.Parse("127.0.0.1");
            IPEndPoint ep = new IPEndPoint(adr, 3000);
            Server.Connect(ep); //完成绑定

            byte[] buffer = new byte[255];
            String data = null;
            int recv = 0;
            recv = Server.Receive(buffer);

            if (recv > 0)
            {
                Console.WriteLine("连接上...");
                Console.WriteLine("从服务器接收数据...");

                data = Encoding.UTF8.GetString(buffer, 0, recv);//ASCII.GetString(buffer, 0, recv);
                Console.WriteLine(data);
                while (true)
                {
                    String input;
                    input = Console.ReadLine();
                    if (input == "exit")
                    {
                        break;
                    }
                    Server.Send(Encoding.ASCII.GetBytes(input));
                }

                Console.WriteLine("连接断开...");
                Server.Shutdown(SocketShutdown.Both);
                Server.Close();
            }
        }
    }
}


多线程版

server端

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;  //可以使用套接字
using System.Threading;  //可以使用多线程


namespace threadtcpclient
{
    class Threadtcpserver
    {
        private Socket server;
        public Threadtcpserver()
        {
            IPAddress local = IPAddress.Parse("127.0.0.1");
            IPEndPoint iep = new IPEndPoint(local, 13000);
            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // 将套接字与本地终结点绑定	
            server.Bind(iep);

            //在本地13000端口号上进行监听	
            server.Listen(20);
            Console.WriteLine("等待客户机进行连接......");
            while (true)
            {
                // 得到包含客户端信息的套接字
                Socket client = server.Accept();

                //创建消息服务线程对象
                ClientThread newclient = new ClientThread(client);

                //把ClientThread 类的ClientService方法委托给线程
                Thread newthread = new Thread(new ThreadStart(newclient.ClientService));

                // 启动消息服务线程
                newthread.Start();
            }
        }
        static void Main(string[] args)
        {
            Threadtcpserver instance = new Threadtcpserver();
        }
    }
    class ClientThread
    {
        //connections变量表示连接数
        public static int connections = 0;
        public Socket service;
        int i;
        public ClientThread(Socket clientsocket)
        {
            //service对象接管对消息的控制
            this.service = clientsocket;
        }
        public void ClientService()
        {
            String data = null;
            byte[] bytes = new byte[1024];
            if (service != null)
            {
                connections++;
            }
            Console.WriteLine("新客户连接建立:{0} 个连接数", connections);
            while ((i = service.Receive(bytes)) != 0)
            {
                data = System.Text.Encoding.UTF8.GetString(bytes, 0, i);
                Console.WriteLine("收到的数据: {0}", data);

                // 处理客户端发来的消息,这里是转化为大写字母
                //data = data.ToUpper();
                byte[] msg = Encoding.UTF8.GetBytes("message from server"); //System.Text.Encoding.UTF8.GetBytes(data);

                // 发送一条应答消息
                service.Send(msg);
                Console.WriteLine("发送的数据: {0}", "message from server");
            }
            //关闭套接字
            service.Close();
            connections--;
            Console.WriteLine("客户关闭连接:{0} 个连接数", connections);
        }
    }
}


client端

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace threadtcpclient
{
    class multithreadclient
    {
        static void Main(string[] args)
        {
            Socket client;
            byte[] buf = new byte[1024];
            string input;
            IPAddress local = IPAddress.Parse("127.0.0.1");
            IPEndPoint iep = new IPEndPoint(local, 13000);
            try
            {
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                client.Connect(iep);
            }
            catch (SocketException)
            {
                Console.WriteLine("无法连接到服务器!");
                return;
            }
            while (true)
            {
                //在控制台上输入一条消息
                input = Console.ReadLine();
                //输入exit,可以断开与服务器的连接
                if (input == "exit")
                {
                    break;
                }
                client.Send(Encoding.UTF8.GetBytes(input));
                //得到实际收到的字节总数
                int rec = client.Receive(buf);
                Console.WriteLine(Encoding.UTF8.GetString(buf, 0, rec));
            }
            Console.WriteLine("断开与服务器的连接......");
            client.Close();
        }
    }
}


socket通信原理





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值