关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » UI前端 »客户端到服务器端的通信过程及原理

    客户端到服务器端的通信过程及原理

    2015-02-26 00:00:00 出处:sumsung753
    分享

    学习任何东西,我们只要搞清楚其原理,就会触类旁通。现在结和我所学,我想总结一下客户端到服务器端的通信过程。只有明白了原理,我们才会明白当我们程序开发过程中错误的问题会出现在那,才会更好的解决问题。

    我们首先要了解一个概念性的词汇:Socket

    socket的英文原义是“孔”或“插座”。作为进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄。(其实就是两个程序通信用的。)socket非常类似于电话的插座。以一个电话网为例。电话的通话双方相当于相互通信的2个程序,电话号码可以当作是IP地址。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码(IP地址),相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接,通信完成。

    以上通信是以两个人通话做为事例来在概的说明了下通信,但是现在假如通信中的一个人是外国人(说英语),一个人是中国人(说普通话),他们俩相互通信的话,都不能听明白对方说的是什么,那么他们的沟通就不能够完成。但是假如我们给一个规定,给通话双方,只能讲普通话,那么双方沟通就没有障碍了。这就引出来了通信协议。

    有两种类型:(Tcp协议与Udp协议):

    Tcp协议与Udp协议是在两硬件设备上进行通信传输的一种数据语法。

    – 流式Socket(STREAM):

    是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低;Tcp:是以流的形式来传的。

    – 数据报式Socket(DATAGRAM):

    是一种无连接的Socket,对应于无连接的UDP服务应用.不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高.Udp:将数据包拆开为若干份编号后来传输。在传输的过程中容易出现数据的丢失。但是传输速度要比TCP的快。

    Socket的通信流程

    Demo:

    服务器端:

    – 申请一个socket (socketWatch)用来监听的

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

    – 开启侦听,等待接授客户端的连接

    – 当有连接时创建一个用于和连接进来的客户端进行通信的socket(socketConnection)

    – 即续监听,等侍下一个客户的连接

    代码如下:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    using System.Net;//IPAdress,IPEndPoint(ip和端口)类
    using System.Net.Sockets;
    using System.Threading;
    using System.IO;
    
    namespace MyChatRoomServer
    {
        public partial class FChatServer : Form
        {
            public FChatServer()
            {
                InitializeComponent();
                TextBox.CheckForIllegalCrossThreadCalls = false;//关闭 对 文本框  的跨线程操作检查
            }
    
            Thread threadWatch = null;//负责监听 客户端 连接请求的 线程
            Socket socketWatch = null;//负责监听的 套接字
    
            private void btnBeginListen_Click(object sender, EventArgs e)
            {
                //创建 服务端 负责监听的 套接字,参数(使用IP4寻址协议,使用流式连接,使用TCP协议传输数据)
                socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
               //获得文本框中的 IP地址对象
                IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
                //创建 包含 ip 和 port 的网络节点对象
                IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
                //将 负责监听 的套接字 绑定到 唯一的IP和端口上
                socketWatch.Bind(endpoint);
                //设置监听队列的长度
                socketWatch.Listen(10);
    
                //创建 负责监听的线程,并传入监听方法
                threadWatch = new Thread(WatchConnecting);
                threadWatch.IsBackground = true;//设置为后台线程
                threadWatch.Start();//开启线程
                ShowMsg("服务器启动监听成功~");
                //IPEndPoint 
                //socketWatch.Bind(
            }
            //保存了服务器端 所有负责和客户端通信的套接字
            Dictionary<string, Socket> dict = new Dictionary<string, Socket>();
            //保存了服务器端 所有负责调用 通信套接字.Receive方法 的线程
            Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
    
            //Socket sokConnection = null;
            /// <summary>
            /// 监听客户端请求的方法
            /// </summary>
            void WatchConnecting()
            {
                while (true)//持续不断的监听新的客户端的连接请求
                {
                    //开始监听 客户端 连接请求,注意:Accept方法,会阻断当前的线程!
                    Socket sokConnection = socketWatch.Accept();//一旦监听到客户端的请求,就返回一个负责和该客户端通信的套接字 sokConnection
                    //sokConnection.Receive
                    //向 列表控件中 添加一个 客户端的ip端口字符串,作为客户端的唯一标识
                    lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
                    //将 与客户端通信的 套接字对象 sokConnection 添加到 键值对集合中,并以客户端IP端口作为键
                    dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);
    
                    //创建 通信线程
                    ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg);
                    Thread thr = new Thread(pts);
                    thr.IsBackground = true;//设置为
                    thr.Start(sokConnection);//启动线程 并为线程要调用的方法RecMsg 传入参数sokConnection
    
                    dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr);//将线程 保存在 字典里,方便大家以后做“踢人”功能的时候用
    
                    ShowMsg("客户端连接成功!" + sokConnection.RemoteEndPoint.ToString());
                    //sokConnection.RemoteEndPoint 中保存的是 当前连接客户端的 Ip和端口
                }
            }
    
            /// <summary>
            /// 服务端 负责监听 客户端 发送来的数据的 方法
            /// </summary>
            void RecMsg(object socketClientPara)
            {
                Socket socketClient = socketClientPara as Socket;
                while (true)
                {
                    //定义一个 接收用的 缓存区(2M字节数组)
                    byte[] arrMsgRec = new byte[1024 * 1024 * 2];
                    //将接收到的数据 存入 arrMsgRec 数组,并返回 真正接收到的数据 的长度
                    int length=-1;
                    try
                    {
                        length = socketClient.Receive(arrMsgRec);
                    }
                    catch (SocketException ex)
                    {
                        ShowMsg("异常:" + ex.Message);
                        //从 通信套接字 集合中 删除 被中断连接的 通信套接字对象
                        dict.Remove(socketClient.RemoteEndPoint.ToString());
                        //从 通信线程    结合中 删除 被终端连接的 通信线程对象
                        dictThread.Remove(socketClient.RemoteEndPoint.ToString());
                        //从 列表中 移除 被中断的连接 ip:Port
                        lbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
                        break;
                    }
                    catch (Exception ex)
                    {
                        ShowMsg("异常:" + ex.Message);
                        break;
                    }
                    if (arrMsgRec[0] == 0)//判断 发送过来的数据 的第一个元素是 0,则代表发送来的是 文字数据
                    {
                        //此时 是将 数组 所有的元素 都转成字符串,而真正接收到的 只有服务端发来的几个字符
                        string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,1, length-1);
                        ShowMsg(strMsgRec);
                    }
                    else if (arrMsgRec[0] == 1)//假如是1 ,则代表发送过来的是 文件数据(图片/视频/文件....)
                    {
                        SaveFileDialog sfd = new SaveFileDialog();//保存文件选择框对象
                        if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)//用户选择文件路径后
                        {
                            string fileSavePath = sfd.FileName;//获得要保存的文件路径
                            //创建文件流,然后 让文件流来 根据路径 创建一个文件
                            using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
                            {
                                fs.Write(arrMsgRec, 1, length-1);
                                ShowMsg("文件保存成功:" + fileSavePath);
                            }
                        }
                    }
                }
            }
    
            //发送消息到客户端
            private void btnSend_Click(object sender, EventArgs e)
            {
                if (string.IsNullOrEmpty(lbOnline.Text))
                {
                    MessageBox.Show("请选择要发送的好友");
                }
                else
                {
                    string strMsg = txtMsgSend.Text.Trim();
                    //将要发送的字符串 转成 utf8对应的字节数组
                    byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
                    //获得列表中 选中的KEY
                    string strClientKey = lbOnline.Text;
                    //通过key,找到 字典集合中对应的 与某个客户端通信的 套接字的 send方法,发送数据给对方
                    try
                    {
                        dict[strClientKey].Send(arrMsg);
                        //sokConnection.Send(arrMsg);
                        ShowMsg("发送了数据出去:" + strMsg);
                    }
                    catch (SocketException ex)
                    {
                        ShowMsg("发送时异常:"+ex.Message);
                    }
                    catch (Exception ex)
                    {
                        ShowMsg("发送时异常:" + ex.Message);
                    }
                }
            }
    
            //服务端群发消息
            private void btnSendToAll_Click(object sender, EventArgs e)
            {
                string strMsg = txtMsgSend.Text.Trim();
                //将要发送的字符串 转成 utf8对应的字节数组
                byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
                foreach (Socket s in dict.Values)
                {
                    s.Send(arrMsg);
                }
                ShowMsg("群发完毕!:)");
            }
    
            #region 显示消息
            /// <summary>
            /// 显示消息
            /// </summary>
            /// <param name="msg"></param>
            void ShowMsg(string msg)
            {
    客户端:

    – 申请一个socket(socketClient)

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

    代码如下:

    using System;
    
    using System.Collections.Generic;
    
    using System.ComponentModel;
    
    using System.Data;
    
    using System.Drawing;
    
    using System.Linq;
    
    using System.Text;
    
    using System.Windows.Forms;
    
    using System.Net.Sockets;
    
    using System.Net;
    
    using System.Threading;
    
    namespace MyChatRoomClient
    
    {
    
        public partial class FChatClient : Form
    
        {
    
            public FChatClient()
    
            {
    
                InitializeComponent();
    
                TextBox.CheckForIllegalCrossThreadCalls = false;
    
            }
    
            Thread threadClient = null; //客户端 负责 接收 服务端发来的数据消息的线程
    
            Socket socketClient = null;//客户端套接字
    
    //客户端发送连接请求到服务器
    
            private void btnConnect_Click(object sender, EventArgs e)
    
            {
    
                IPAddress address = IPAddress.Parse(txtIP.Text.Trim());//获得IP
    
                IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));//网络节点
    
    //创建客户端套接字
    
                socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
                //向 指定的IP和端口 发送连接请求
    
                socketClient.Connect(endpoint);
    
                //客户端 创建线程 监听服务端 发来的消息
    
                threadClient = new Thread(RecMsg);
    
                threadClient.IsBackground = true;
    
                threadClient.Start();
    
            }
    
            /// <summary>
    
    /// 监听服务端 发来的消息
    
    /// </summary>
    
            void RecMsg()
    
            {
    
                while (true)
    
                {
    
                    //定义一个 接收用的 缓存区(2M字节数组)
    
                    byte[] arrMsgRec = new byte[1024 * 1024 * 2];
    
                    //将接收到的数据 存入 arrMsgRec 数组,并返回 真正接收到的数据 的长度
    
                   int length=  socketClient.Receive(arrMsgRec);
    
                    //此时 是将 数组 所有的元素 都转成字符串,而真正接收到的 只有服务端发来的几个字符
    
                   string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 0, length);
    
                    ShowMsg(strMsgRec);
    
                }
    
            }
    
            void ShowMsg(string msg)
    
            {
    
                txtMsg.AppendText(msg + "rn");
    
            }
    
        }
    
    }

    通信过程图

    通过以上流程图我们可以看出,客户端与服务器端之间的一个基本通信流程,概括一下Socket 一般应用模式(客户端和服务器端)的作用:

    服务器端:最少有两个socket,一个是服务端负责监听客户端发来连接请求,但不负责与请求的客户端通信,另一个是每当服务器端成功接收到客户端时,但在服务器端创建一个用与请求的客户端进行通信的socket.

    客户端:指定要连接的服务器端地址和端口,通过创建一个socket对象来初始化一个到服务器端的TCP连接。

    其实很早就想写下这篇总结了,但是由于工作较忙,一直推迟到现在。这篇总结也是为我接下来要写的浏览器与Iis服务器的通信过程和ASP.Net页面生命周期做一个铺垫,现在终于写完了,来和大家一起分享一下,不完善的地方,我将在以后的工作和学习过程中慢慢补充。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    IT头条

    无线路由大厂 TP-Link突然大裁员:补偿N+3

    02:39

    Meta 千万美金招募AI高级人才

    00:22

    更容易爆炸?罗马仕充电宝被北京多所高校禁用,公司紧急回应

    17:19

    天衍”量子计算云平台,“超算+量算” 告别“算力孤岛时代”

    18:18

    华为Pura80系列新机预热,余承东力赞其复杂光线下的视频拍摄实力

    01:28

    技术热点

    MySQL基本调度策略浅析

    MySQL使用INSERT插入多条记录

    SQL Server高可用的常见问题

    3D立体图片展示幻灯片JS特效

    windows 7上网看视频出现绿屏的原因及解决方法

    windows 7 64位系统的HOSTS文件在哪里?想用它加快域名解析

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。