关闭 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页面生命周期做一个铺垫,现在终于写完了,来和大家一起分享一下,不完善的地方,我将在以后的工作和学习过程中慢慢补充。

    上一篇返回首页 下一篇

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

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

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

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

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

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

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

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