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

我们首先要了解一个概念性的词汇: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)

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

代码如下:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.ComponentModel;

  4. using System.Data;

  5. using System.Drawing;

  6. using System.Linq;

  7. using System.Text;

  8. using System.Windows.Forms;

  9. using System.Net;//IPAdress,IPEndPoint(ip和端口)类

  10. using System.Net.Sockets;

  11. using System.Threading;

  12. using System.IO;

  13. namespace MyChatRoomServer

  14. {

  15. public partial class FChatServer : Form

  16. {

  17. public FChatServer()

  18. {

  19. InitializeComponent();

  20. TextBox.CheckForIllegalCrossThreadCalls = false;//关闭 对 文本框 的跨线程操作检查

  21. }

  22. Thread threadWatch = null;//负责监听 客户端 连接请求的 线程

  23. Socket socketWatch = null;//负责监听的 套接字

  24. private void btnBeginListen_Click(object sender, EventArgs e)

  25. {

  26. //创建 服务端 负责监听的 套接字,参数(使用IP4寻址协议,使用流式连接,使用TCP协议传输数据)

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

  28. //获得文本框中的 IP地址对象

  29. IPAddress address = IPAddress.Parse(txtIP.Text.Trim());

  30. //创建 包含 ip 和 port 的网络节点对象

  31. IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));

  32. //将 负责监听 的套接字 绑定到 唯一的IP和端口上

  33. socketWatch.Bind(endpoint);

  34. //设置监听队列的长度

  35. socketWatch.Listen(10);

  36. //创建 负责监听的线程,并传入监听方法

  37. threadWatch = new Thread(WatchConnecting);

  38. threadWatch.IsBackground = true;//设置为后台线程

  39. threadWatch.Start();//开启线程

  40. ShowMsg("服务器启动监听成功~");

  41. //IPEndPoint

  42. //socketWatch.Bind(

  43. }

  44. //保存了服务器端 所有负责和客户端通信的套接字

  45. Dictionary<string, Socket> dict = new Dictionary<string, Socket>();

  46. //保存了服务器端 所有负责调用 通信套接字.Receive方法 的线程

  47. Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();

  48. //Socket sokConnection = null;

  49. /// <summary>

  50. /// 监听客户端请求的方法

  51. /// </summary>

  52. void WatchConnecting()

  53. {

  54. while (true)//持续不断的监听新的客户端的连接请求

  55. {

  56. //开始监听 客户端 连接请求,注意:Accept方法,会阻断当前的线程!

  57. Socket sokConnection = socketWatch.Accept();//一旦监听到客户端的请求,就返回一个负责和该客户端通信的套接字 sokConnection

  58. //sokConnection.Receive

  59. //向 列表控件中 添加一个 客户端的ip端口字符串,作为客户端的唯一标识

  60. lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());

  61. //将 与客户端通信的 套接字对象 sokConnection 添加到 键值对集合中,并以客户端IP端口作为键

  62. dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);

  63. //创建 通信线程

  64. ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg);

  65. Thread thr = new Thread(pts);

  66. thr.IsBackground = true;//设置为

  67. thr.Start(sokConnection);//启动线程 并为线程要调用的方法RecMsg 传入参数sokConnection

  68. dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr);//将线程 保存在 字典里,方便大家以后做“踢人”功能的时候用

  69. ShowMsg("客户端连接成功!" + sokConnection.RemoteEndPoint.ToString());

  70. //sokConnection.RemoteEndPoint 中保存的是 当前连接客户端的 Ip和端口

  71. }

  72. }

  73. /// <summary>

  74. /// 服务端 负责监听 客户端 发送来的数据的 方法

  75. /// </summary>

  76. void RecMsg(object socketClientPara)

  77. {

  78. Socket socketClient = socketClientPara as Socket;

  79. while (true)

  80. {

  81. //定义一个 接收用的 缓存区(2M字节数组)

  82. byte[] arrMsgRec = new byte[1024 * 1024 * 2];

  83. //将接收到的数据 存入 arrMsgRec 数组,并返回 真正接收到的数据 的长度

  84. int length=-1;

  85. try

  86. {

  87. length = socketClient.Receive(arrMsgRec);

  88. }

  89. catch (SocketException ex)

  90. {

  91. ShowMsg("异常:" + ex.Message);

  92. //从 通信套接字 集合中 删除 被中断连接的 通信套接字对象

  93. dict.Remove(socketClient.RemoteEndPoint.ToString());

  94. //从 通信线程 结合中 删除 被终端连接的 通信线程对象

  95. dictThread.Remove(socketClient.RemoteEndPoint.ToString());

  96. //从 列表中 移除 被中断的连接 ip:Port

  97. lbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());

  98. break;

  99. }

  100. catch (Exception ex)

  101. {

  102. ShowMsg("异常:" + ex.Message);

  103. break;

  104. }

  105. if (arrMsgRec[0] == 0)//判断 发送过来的数据 的第一个元素是 0,则代表发送来的是 文字数据

  106. {

  107. //此时 是将 数组 所有的元素 都转成字符串,而真正接收到的 只有服务端发来的几个字符

  108. string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,1, length-1);

  109. ShowMsg(strMsgRec);

  110. }

  111. else if (arrMsgRec[0] == 1)//如果是1 ,则代表发送过来的是 文件数据(图片/视频/文件....)

  112. {

  113. SaveFileDialog sfd = new SaveFileDialog();//保存文件选择框对象

  114. if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)//用户选择文件路径后

  115. {

  116. string fileSavePath = sfd.FileName;//获得要保存的文件路径

  117. //创建文件流,然后 让文件流来 根据路径 创建一个文件

  118. using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))

  119. {

  120. fs.Write(arrMsgRec, 1, length-1);

  121. ShowMsg("文件保存成功:" + fileSavePath);

  122. }

  123. }

  124. }

  125. }

  126. }

  127. //发送消息到客户端

  128. private void btnSend_Click(object sender, EventArgs e)

  129. {

  130. if (string.IsNullOrEmpty(lbOnline.Text))

  131. {

  132. MessageBox.Show("请选择要发送的好友");

  133. }

  134. else

  135. {

  136. string strMsg = txtMsgSend.Text.Trim();

  137. //将要发送的字符串 转成 utf8对应的字节数组

  138. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);

  139. //获得列表中 选中的KEY

  140. string strClientKey = lbOnline.Text;

  141. //通过key,找到 字典集合中对应的 与某个客户端通信的 套接字的 send方法,发送数据给对方

  142. try

  143. {

  144. dict[strClientKey].Send(arrMsg);

  145. //sokConnection.Send(arrMsg);

  146. ShowMsg("发送了数据出去:" + strMsg);

  147. }

  148. catch (SocketException ex)

  149. {

  150. ShowMsg("发送时异常:"+ex.Message);

  151. }

  152. catch (Exception ex)

  153. {

  154. ShowMsg("发送时异常:" + ex.Message);

  155. }

  156. }

  157. }

  158. //服务端群发消息

  159. private void btnSendToAll_Click(object sender, EventArgs e)

  160. {

  161. string strMsg = txtMsgSend.Text.Trim();

  162. //将要发送的字符串 转成 utf8对应的字节数组

  163. byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);

  164. foreach (Socket s in dict.Values)

  165. {

  166. s.Send(arrMsg);

  167. }

  168. ShowMsg("群发完毕!:)");

  169. }

  170. #region 显示消息

  171. /// <summary>

  172. /// 显示消息

  173. /// </summary>

  174. /// <param name="msg"></param>

  175. void ShowMsg(string msg)

  176. {

  • 客户端:

– 申请一个socket(socketClient)

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

代码如下:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.ComponentModel;

  4. using System.Data;

  5. using System.Drawing;

  6. using System.Linq;

  7. using System.Text;

  8. using System.Windows.Forms;

  9. using System.Net.Sockets;

  10. using System.Net;

  11. using System.Threading;

  12. namespace MyChatRoomClient

  13. {

  14. public partial class FChatClient : Form

  15. {

  16. public FChatClient()

  17. {

  18. InitializeComponent();

  19. TextBox.CheckForIllegalCrossThreadCalls = false;

  20. }

  21. Thread threadClient = null; //客户端 负责 接收 服务端发来的数据消息的线程

  22. Socket socketClient = null;//客户端套接字

  23. //客户端发送连接请求到服务器

  24. private void btnConnect_Click(object sender, EventArgs e)

  25. {

  26. IPAddress address = IPAddress.Parse(txtIP.Text.Trim());//获得IP

  27. IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));//网络节点

  28. //创建客户端套接字

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

  30. //向 指定的IP和端口 发送连接请求

  31. socketClient.Connect(endpoint);

  32. //客户端 创建线程 监听服务端 发来的消息

  33. threadClient = new Thread(RecMsg);

  34. threadClient.IsBackground = true;

  35. threadClient.Start();

  36. }

  37. /// <summary>

  38. /// 监听服务端 发来的消息

  39. /// </summary>

  40. void RecMsg()

  41. {

  42. while (true)

  43. {

  44. //定义一个 接收用的 缓存区(2M字节数组)

  45. byte[] arrMsgRec = new byte[1024 * 1024 * 2];

  46. //将接收到的数据 存入 arrMsgRec 数组,并返回 真正接收到的数据 的长度

  47. int length= socketClient.Receive(arrMsgRec);

  48. //此时 是将 数组 所有的元素 都转成字符串,而真正接收到的 只有服务端发来的几个字符

  49. string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 0, length);

  50. ShowMsg(strMsgRec);

  51. }

  52. }

  53. void ShowMsg(string msg)

  54. {

  55. txtMsg.AppendText(msg + "\r\n");

  56. }

  57. }

  58. }

通信过程图

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

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

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

转载自:http://www.codeceo.com/article/client-to-server.html

客户端到服务器端的通信过程及原理(很清晰,保证看后顿悟)相关推荐

  1. 客户端到服务器端的通信过程及原理(由浅入深,轻松理解)

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

  2. 客户端发送协议包给服务器,TCP协议的客户端与服务器的通信过程

    使用TCP时,客户端与服务器端的通信流程 服务器初始化 1)调用socket,创建文件描述符fd 2) 调用bind将fd与服务器的IP与PORT绑定 3)调用listen将套接字设为监听模式,准备接 ...

  3. 客户端到服务器的通信过程

    客户端到服务器的通信过程 认识socket socket的英文原意为:孔.插座.当然作为进程通信机制,取插座的意思.通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄 ...

  4. 对等网主机的通信过程以及原理,很简单

    大家好,我们今天来分享一下对等网主机的通信过程以及原理 关于对等网的解释: 对等网采用分散管理的方式,网络中的每台计算机既作为客户机又可作为服务器来工作,每个用户都管理自己机器上的资源. 看这个网络拓 ...

  5. 【网络通信与信息安全】之深入解析两台主机之间的通信过程和原理

    一.前言 本文通过在 Docker 容器中执行命令,来深入了解两台主机之间的通信过程.阅读完本文,您将熟悉以下内容: Docker 的基本操作: 创建 socket 并发送 HTTP 请求: 路由表. ...

  6. Java通过socket实现客户端和服务器端的通信

    在学习Java的socket通信时,老师布置的一道作业题,借此机会也对相关的知识进行梳理,题目如下: 编写客户服务器端程序,使用Socket技术实现通信,双方约定通信端口为6789.服务器端功能:当收 ...

  7. Java实现客户端与服务器端的通信

    客户端与服务器端交互数据需要进行通信,本文介绍安卓客户端是如何与服务器端进行通信的,包括客户端连接服务器端.客户端向服务器端发送请求.将请求信息封装成请求单元.将请求单元存放到队列.从队列中获取请求单 ...

  8. python客户端与服务器端通信_python客户端与服务器端的通信

    服务器端: import java.io.*;import java.net.*;public class xin {ServerSocket mSS;Socket mSocket;static in ...

  9. redis源码客户端和服务端通信过程

    最近想学习一下redis源码,先看一下redis通信流程.由于功力有限,不足之处望大家指正.服务端和客户端通信,一般都是服务端先启动,那先从服务端的源码看起. 首先启动服务端会做一些初始化动作,初始化 ...

最新文章

  1. YOLOv4 中的 Mish 激活函数
  2. ASCII、Unicode、GBK、UTF-8之间的关系
  3. js怎么取到遍历中的特定值_LeetCode 1028 hard 从先序遍历还原二叉树 Python解题记录...
  4. Bootstrap手机网站开发案例
  5. Visual Studio 2017 调试 windows server 2016 Docker Container
  6. [转发] 老叶观点:MySQL开发规范之我见
  7. SpringCloud_005_Maven中的Scope详解_准备01
  8. C++中const的一些知识点
  9. 在VS 2010中查询和导航代码
  10. java动态代理_Java核心 -- 动态代理
  11. 基于微信小程序的停车位预约系统设计与实现毕业设计毕设开题报告
  12. 于的繁体字有几种写法_于字书法繁体字怎么写
  13. 计算机扩大C盘分区容量,教你在保留数据的情况下,如何把C盘容量无限扩大!...
  14. 什么是视频编码 编解码技术及压缩技术
  15. 基于Angularjs框架实现HTML5在线查看OFD文件
  16. ensp 交换机与路由器ospf_—华为数通eNSP模拟实验15:交换机对接路由器
  17. AIX系统的磁带备份
  18. Flutter BLoC
  19. i春秋-Web(一)
  20. Hyperledger(超级账本)

热门文章

  1. 适合编程初学者的开源博客系统(Go语言版)
  2. PMP的在国内有用吗?含金量多高?
  3. 泊松分布期望和方差推导
  4. 雪王刚启用,1000多家加盟商抵制,说太累了low
  5. GitHub是什么?--GitHub介绍和推荐
  6. AutoHotkey中的变量名 全局与局部冲突问题 - double-ref 双重引用
  7. 2023年PMP考试内容有哪些?怎么备考?
  8. rk3588硬件构成-rock5b
  9. Silverlight游戏研发手记:(一)差集运算在SLG战斗范围设定中的应用
  10. NFS服务器搭建及配置