C# 网络编程

C# 网络编程

互联网通过 IP 定位电脑。

IP地址

  • 每台联网的电脑都有一个唯一的IP。(如果有多个网卡,一个网卡,一个IP)
  • IPV4:分为4段,每段8bit,范围是0-255
  • 用户本地网卡IP:127.0.0.1
  • IPV6:128bits

在电脑中通过 Port 来定位程序。

常用端口:21FTP, 25SMTP, 110POP3, 80HTTP, 443HTTPS

C# 网络编程_第1张图片

程序之间通过 协议 定义通信数据的格式。

网卡接受到数据以后,根据端口号,把数据发送给不同的程序。

Socket

定义:

是一种通信机制,用于描述IP地址和port,是一个通信链的句柄。用于两个程序的通信。

Socket类似打电话。电话通信的双方相当于相互通信的2个程序,电话号码是IP。用户通话前,首先要占有一部电话(socket),同时要知道对方的号码(对方有一个固定的socket),然后拨号(发出连接请求)。对方假如在场并空闲,拿起电话筒,双方就可以通话(连接成功)。双方通话的过程,相当于一方向socket发送数据,一方从socket接收数据。通话结束后,一方挂起电话,相当于关闭socket,撤销连接,


类型:

流式Socket(Stream):是一种面向连接的Socket,tcp协议用stream socket,安全,效率低。

browser和server之间使用的tcp协议(流式socket)

C# 网络编程_第2张图片

数据报Socket(Datagram):无连接的Socket,udp使用报式Socket,不安全(丢失,顺序混乱,在接收端要分析重排序,要求重发等),效率高。

chatting项目示例

ChattingServer示例:

创建一个winform程序
C# 网络编程_第3张图片
public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //服务器段 监听socket
        Socket sListen = null;

        private void btnListen_Click(object sender, EventArgs e)
        {
            //1.创建监听套接字 使用 ip4协议,流式传输,TCP连接
            sListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2.绑定端口
            //2.1获取网络节点(IPEndPoint)对象
            IPAddress ip = IPAddress.Parse(txtIP.Text);
            IPEndPoint endPoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
            //2.2绑定端口(其实内部 就向系统的 端口表中 注册 了一个端口,并指定了当前程序句柄)
            sListen.Bind(endPoint);
            //2.3设置监听队列
            //The maximum length of the pending connections queue. : 10
            sListen.Listen(10);
            //2.4 开始监听,block直到有连接请求,返回值 : A Socket for a newly created connection.
            sListen.Accept();
        }
程序运行起来以后,accept方法阻塞了当前线程,窗口不能再拖动。
用browser进行测试,比如程序监听127.0.0.1:11111, 在浏览器输入这个ip:port. 这时候Accept方法返回了,窗口又可以拖动了。
创建一个线程来监听。
        //服务器段 监听socket
        Socket sListen = null;
        //监听线程
        Thread threadListen = null;

        private void btnListen_Click(object sender, EventArgs e)
        {
            //1.创建监听套接字 使用 ip4协议,流式传输,TCP连接
            sListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2.绑定端口
            //2.1获取网络节点(IPEndPoint)对象
            IPAddress ip = IPAddress.Parse(txtIP.Text);
            IPEndPoint endPoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
            //2.2绑定端口(其实内部 就向系统的 端口表中 注册 了一个端口,并指定了当前程序句柄)
            sListen.Bind(endPoint);
            //2.3设置监听队列
            //The maximum length of the pending connections queue. : 10
            sListen.Listen(10);
            ////2.4 开始监听,block知道有连接请求,返回值 : A Socket for a newly created connection.
            //sListen.Accept();
            threadListen = new Thread(ListenConnection);
            threadListen.IsBackground = true;
            threadListen.Start();
        }

        private void ListenConnection() {
            sListen.Accept();
        }

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        //服务器段 监听socket
        Socket sListen = null;
        //监听线程
        Thread threadListen = null;

        private void btnListen_Click(object sender, EventArgs e)
        {
            //1.创建监听套接字 使用 ip4协议,流式传输,TCP连接
            sListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2.绑定端口
            //2.1获取网络节点(IPEndPoint)对象
            IPAddress ip = IPAddress.Parse(txtIP.Text);
            IPEndPoint endPoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
            //2.2绑定端口(其实内部 就向系统的 端口表中 注册 了一个端口,并指定了当前程序句柄)
            sListen.Bind(endPoint);
            //2.3设置监听队列
            //The maximum length of the pending connections queue. : 10
            sListen.Listen(10);
            ////2.4 开始监听,block知道有连接请求,返回值 : A Socket for a newly created connection.
            //sListen.Accept();
            threadListen = new Thread(ListenConnection);
            threadListen.IsBackground = true;
            threadListen.Start();
            ShowMsg("server initialized");
        }

        private void ListenConnection() {
            sListen.Accept();
            ShowMsg("one client connected");
        }

        private void ShowMsg(String str) {
            txtMsg.AppendText(str + "\n");
        }
    }

ChattingClient示例:

winform程序:

C# 网络编程_第4张图片

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

        Socket socClient = null;

        private void btnConnectServer_Click(object sender, EventArgs e)
        {
            socClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //获得要连接的 server 节点
            IPAddress ipServer = IPAddress.Parse(txtIP.Text);
            IPEndPoint endpoint = new IPEndPoint(ipServer, int.Parse(txtPort.Text));
            //向Server发出连接请求
            socClient.Connect(endpoint);
        }
    }
测试:先运行服务器端,start listen,然后客户端project右键,debug,start new instance。连接server。

客户端向Server发消息:

Server端:

        private void ListenConnection() {
            //2.4 开始监听,block知道有连接请求,返回值 : A Socket for a newly created connection. 
            //返回通信socket,这个socket 监听 客户端 传来的消息
            Socket socMsg = sListen.Accept();
            ShowMsg("one client connected");

            //3 通信socket 监听client发来的消息
            //3.1 消息buffer
            byte[] buffer = new byte[1024*1024*1];//1M
            //3.2 接收client send方法发来的消息,并存放到buffer中
            //Receive方法也会block当前线程,直到接收到client send方法发来的消息
            socMsg.Receive(buffer);
            //3.3 将接收到的byte数组转成字符串
            string strMsg = System.Text.Encoding.UTF8.GetString(buffer);
            //3.4 将str显示到textbox
            ShowMsg(strMsg);
        }
Client端:
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            string strMsg = txtMsg.Text.Trim();
            byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
            socClient.Send(arrMsg);
        }
上面的程序,server端只receive一次client端send来的消息。

Server循环接受消息:

server端:建立一个通信线程,专门负责接受client发来的msg
        private void ListenConnection() {
            //2.4 开始监听,block知道有连接请求,返回值 : A Socket for a newly created connection. 
            //返回通信socket,这个socket 监听 客户端 传来的消息
            Socket socMsg = sListen.Accept();
            ShowMsg("one client connected");
            // 2.5 建立通信线程
            Thread threadMsg = new Thread(ReceiveMsg);
            threadMsg.IsBackground = true;
            threadMsg.Start(socMsg);
        }

        bool isReceive = true;
        private void ReceiveMsg(object obj) {
            Socket socMsg = obj as Socket;
            //3 通信socket 监听client发来的消息
            //3.1 消息buffer
            byte[] buffer = new byte[1024 * 1024 * 1];//1M
            //3.2 接收client send方法发来的消息,并存放到buffer中
            //Receive方法也会block当前线程,直到接收到client send方法发来的消息
            while (isReceive) {
                socMsg.Receive(buffer);
                //3.3 将接收到的byte数组转成字符串
                string strMsg = System.Text.Encoding.UTF8.GetString(buffer);
                //3.4 将str显示到textbox
                ShowMsg(strMsg);
            }
        }

Server向client发消息:

在Server端 : Form中加一个ListBox显示连接上的client的ip和port。
        Dictionary dictCon = new Dictionary();
        private void ListenConnection() {
            //2.4 开始监听,block知道有连接请求,返回值 : A Socket for a newly created connection. 
            //返回通信socket,这个socket 监听 客户端 传来的消息
            Socket socMsg = sListen.Accept();
            //将当前连接上的client的ip和port,保存到Dictionary中,并显示到listbox中。
            listBoxClient.Items.Add(socMsg.RemoteEndPoint.ToString());
            dictCon.Add(socMsg.RemoteEndPoint.ToString(), socMsg);
            ShowMsg("one client connected");
            // 2.5 建立通信线程
            Thread threadMsg = new Thread(ReceiveMsg);
            threadMsg.IsBackground = true;
            threadMsg.Start(socMsg);
        }

在Server端 :
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            string endpointStr = null;
            if (listBoxClient.SelectedItem != null) {
                endpointStr = listBoxClient.SelectedItem.ToString();
                if (dictCon.ContainsKey(endpointStr))
                {
                    Socket sClient = dictCon[endpointStr];
                    //使用 指定 socket将 字符串 发送到 指定的client
                    sClient.Send(System.Text.Encoding.UTF8.GetBytes(txtInput.Text.Trim() +"\n"));
                    ShowMsg("to " + endpointStr + " : " + txtInput.Text.Trim());
                    txtInput.Text = string.Empty;
                }
            }
        }
在client端:接受服务器发来的msg
        Thread threadMsg = null;
        private void btnConnectServer_Click(object sender, EventArgs e)
        {
            socClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //获得要连接的 server 节点
            IPAddress ipServer = IPAddress.Parse(txtIP.Text);
            IPEndPoint endpoint = new IPEndPoint(ipServer, int.Parse(txtPort.Text));
            //向Server发出连接请求
            socClient.Connect(endpoint);
            ShowMsg("connected to server");

            threadMsg = new Thread(ReceiveMsg);
            threadMsg.IsBackground = true;
            threadMsg.Start();
        }

        bool isReceive = true;
        private void ReceiveMsg() {
            //3 通信socket 监听client发来的消息
            //3.1 消息buffer
            byte[] buffer = new byte[1024 * 1024 * 1];//1M
            //3.2 接收client send方法发来的消息,并存放到buffer中
            //Receive方法也会block当前线程,直到接收到client send方法发来的消息
            while (isReceive) {
                socClient.Receive(buffer);
                //3.3 将接收到的byte数组转成字符串
                string strMsg = System.Text.Encoding.UTF8.GetString(buffer);
                //3.4 将str显示到textbox
                ShowMsg("From Server : " + strMsg);
            }
        }

小结C# 网络编程_第5张图片

Server端的监听socket需要binding 一个 endpoint(ip+port)。
通信socket不需要bind。

Server循环Accept客户端的请求:

在Server端修改ListenConnection方法:

        Dictionary dictCon = new Dictionary();
        bool isWatch = true;
        private void ListenConnection() {
            //2.4 开始监听,block知道有连接请求,返回值 : A Socket for a newly created connection. 
            //返回通信socket,这个socket 监听 客户端 传来的消息
            while(isWatch){
                Socket socMsg = sListen.Accept();
                //将当前连接上的client的ip和port,保存到Dictionary中,并显示到listbox中。
                listBoxClient.Items.Add(socMsg.RemoteEndPoint.ToString());
                dictCon.Add(socMsg.RemoteEndPoint.ToString(), socMsg);
                ShowMsg("one client connected");
                // 2.5 建立通信线程
                Thread threadMsg = new Thread(ReceiveMsg);
                threadMsg.IsBackground = true;
                threadMsg.Start(socMsg);
            }
        }

为所有网络操作捕捉exception:

Server端:

你可能感兴趣的:(C#)