【转】C# Socket编程(5)使用TCP Socket
【转自:https://www.cnblogs.com/IPrograming/archive/2012/10/18/CSharp_Socket_5.html】
TCP 协议(Transmission Control Protocol,传输控制协议)是TCP/IP体系中面向连接(connection oriented)的传输层(transport layer),TCP协议能够检测和恢复IP层提供的主机到主机的信道中可能发生的报文丢失、重复以及其他错误。由于TCP协议是一种面向连接协议:在使用它进行通信之前,两个应用程序之间首先要建立一个TCP连接。TCP能够在网络中提供双工和可靠的的服务。
1.TCP概述
通信双方建立了TCP连接后,双方就可以相互发送数据了。TCP负责把用户数据(字节流)按照一定格式和长度组成多个数据报进行发送,然后在接到数据报之后分解按顺序重新组装和恢复用户数据。利用TCP传输数据时,数据时以字节的形式进行传输的。客户端和服务端建立连接后,发送数据方需要先将数据转换为字节流,然后将字节流发送到对方。TCP协议主要有以下特点:
- 是面向连接的传输层协议
- 每个TCP连接只能有两个端点,且只能一对一通信
- 通过TCP连接传送数据能够保证报文的完整和准确性
- 数据只能够以字节流的形式传输
- 传输的数据无消息边界
2.在.NET平台TCP应用的工作模式
在.NET平台下开发TCP应用程序,框架提供两种工作方式,①同步工作方式 ②异步工作方式。这里所说的同步工作方式和异步工作方式和线程间的同步并不是一个概念。线程间的同步指的是不同线程或其他共享资源具有先后关联的关系;而同步TCP和异步TCP指的是TCP编程中采用的两种不同的工作方式,即:从执行到方式、接收或监听语句时,程序是否继续往下执行,继续执行的就是异步TCP,如果程序阻塞那就是同步TCP。
与同步工作方式和异步工作方式相对应,利用Socket类开发应用.NET框架也提供了相应的编程方式:分别是同步Socket编程和异步Socket编程。为了简化编程的复杂度,.NET将Socket类进行进一步的封装,提供了两个类:TcpClient类和TcpListener类,这两个类也分别提供同步和异步工作方式的API。
2.1 了解TcpListener和TcpClient
通过前面我们知道:TcpClient类和TcpListener类简化了Socket编程的复杂度,但是要注意:TcpClient和TcpListener这两个类只支持标准协议编程,如果需要编写非标准协议的应用程序,只能使用Socket来实现。
TcpClient类用于提供本机主机和远程主机的连接信息,而TcpListener类则用于监听客户端的请求(这两个类的更多信息可以参考MSDN类库,文中已经给出连接,这里就不在赘述了)。当Socket通信双发建立了连接后,创建了TcpClient对象,就可以使用该对象的GetStream()方法得到NetworkStream对象,然后再利用网络流对象向远程主机发送或接收流数据。
3.解决TCP的无消息边界问题
我们知道网络数据传输是基于流的,在采用TCP通信保证了我们接收和发送数据顺序和完整性,但是在实际的网络传输过程可能会出现发送方和接收方消息不一致的情况。例如:第一次发送的数据为“123456”,第二次发送的数据为“ABCDEF”,有时候可能会出现这种情况:“123456ABCDEF”同时接收了;或者是先接收“123456ABC”,然后在接收“DEF”等情况。之所以出现这种情况是因为:TCP是一种以字节流形式传输的、无消息边界的协议,由于网络中不确定因数的影响,因此不能够保证每个Send方法发送的数据被对应的Recive读取。所以在实际的Socket应用程序开发是必须要考虑消息边界的问题否则就有可能出现数据错误等问题。解决TCP消息边界一般使用下面的三种方式,我们可以根据场景的不同选用不同的方式。
3.1 发送固定长度的消息
这种方式适用于消息长度固定的场景。具体实现时可以使用BinaryReader/BinaryWriter对象每次向网络流,发送/读取一个固定长度的数据即可。例如每次发送一个int类型的32位整数。
TcpClient client = new TcpClient("www.baidu.com", );
NetworkStream m_NetStream = client.GetStream();
BinaryWriter bw = new BinaryWriter(m_NetStream, Encoding.UTF8);
bw.Write();
3.2 将消息长度与消息一起发送
这种方式一般在每次发送消息的前面用4个字节表面本次消息的长度,然后将包含消息长度的消息发送给对方;对方收到消息后,首先从消息的前四个字节读取消息长度,然后根据消息长度值接收发送方发送的数据。这种方法适用于任何场合,在这里我们可以利用BinaryReader和BinaryWriter对象来对NetworkStream进行进一步的封装,当我们使用BinaryWriter对象调用Write(+18重载)方法向网络流写入数据时,该方法会自动计算发出送数据占用的字节数,并使用4(根据发送数据类型)个字节附加到字符串前面;然后另一方使用BinaryReader对象的对应于BinaryWriter对象的Write()方法读取数据时,它会首先读取数据的长度,并自动根据数据前缀读取指定长度的数据。
3.3 使用特殊标记分隔消息
这种方式适用于消息中不包含特殊标记的场合。例如:在每个命令后面添加回车换行(\r\n)符号作为分隔符的场合。如果对于字符串处理,实现这种方法最简便的途径是使用StreamWriter对象和StreamReader对象。发送时使用StreamWriter对象的WriteLine()方法将发送的字符串写入网络流,接收方只需需要调用StreamReader对象的ReadLine()方法将以回车换行符作为分隔符的字符串从网络流中读取即可。
4.同步TCP Socket 示例程序
经过前面知识的积淀,现在我们直接通过创建一个简单的聊天程序来基于同步TCP Socket网络聊天程序,程序的实现比较简单,服务器接收多个客户端连接,客户端和客户端直接通过服务器中转消息达到相互通信的目的。客户端和服务器直接的消息交互使用JSON来进行传递,在这里使用了第三方的JSON库JSON.NET(关于JSON.NET的使用细节参考:JSON.NET使用小结)。下面是程序运行的效果:
运行服务端:

打开多个客户端,在在线列表中选择聊天对象,发送聊天信息:

【转】C# Socket编程(5)使用TCP Socket的更多相关文章
- C# Socket编程(5)使用TCP Socket
TCP 协议(Transmission Control Protocol,传输控制协议)是TCP/IP体系中面向连接(connection oriented)的传输层(transport layer) ...
- Windows下C语言的Socket编程例子(TCP和UDP)
原文:Windows下C语言的Socket编程例子(TCP和UDP) 刚刚学windows编程,所以想写学习笔记,这是一个简单的Socket程序例子,开发环境是vc6: 首先是TCP server端: ...
- 《java入门第一季》之Socket编程通信和TCP协议通信图解
Socket编程通信图解原理: TCP协议通信图解
- Socket编程实践(6) --TCP服务端注意事项
僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...
- Windows Socket编程精华《TCP通信服务器》
1.网络中进程之间如何通信? 首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的.其实TCP/IP协议族已经帮我们解决了这个问 ...
- python socket 编程之一:编写socket的基本步骤
一.socket 编写server的步骤: 1.第一步是创建socket对象.调用socket构造函数.如: socket = socket.socket( family, type ) family ...
- Socket编程基础——面向连接TCP
WinSock是Windows环境下的网络编程接口,它最初是基于Unix环境下的BSD Socket,是一个与网络协议无关的编程接口.WinSock包含两个主要版本,即WinSock1和WinSock ...
- Socket 编程中,TCP 流的结束标志与粘包问题
因为 TCP 本身是无边界的协议,因此它并没有结束标志,也无法分包. socket和文件不一样,从文件中读,读到末尾就到达流的结尾了,所以会返回-1或null,循环结束,但是socket是连接两个主机 ...
- Socket编程:UDP和TCP概论及案例
网络编程的三要素: 1.IP地址 2.端口 3.协议 什么是Socket? Socket就是通信链路的端点称"套接词". 基于TCP协议的Socket网络通信: 用来实现双向安全 ...
随机推荐
- Java多线程基础知识总结笔记
本篇笔记记录一些在Java多线程编程中常见的关键字,比较简单和基础的就不写太详细了. 一.Thread类(其实也是应用了Runnable接口)和Runnable接口(只有一个run方法,应用该类必须重 ...
- React Native之持久化存储(AsyncStorage、react-native-storage)的使用
AsyncStorage是一个简单的.异步的.持久化的Key-Value存储系统,它对于App来说是全局性的.这是官网上对它的介绍.可以知道,这个asyncstorage也是以键值对的形式进行存储数据 ...
- 解决com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Specified key was too long; max key length is 767 bytes异常
错误截图: 解决方法: 用root进入mysql终端,执行以下命令: alter database hive character set latin1;
- UVA 10214 Trees in a Wood(欧拉函数)
题意:给你a.b(a<=2000,b<=2000000),问你从原点可以看到范围在(-a<=x<=a,-b<=y<=b)内整数点的个数 题解:首先只需要计算第一象限 ...
- MFC 任务管理器设计
在学习界面设计,因为对一些控件不熟悉,所以也参考了别人的.核心代码如下. void CTasklistDlg::Expand() { m_list.SetExtendedStyle(LVS_EX_GR ...
- POJ 入门
先复习一下C的一些基本概念 1.C标准化输出:scanf int m,n; scanf("%d%d",&n,&m); 实际上scanf是有返回值的,且返回值的类型为 ...
- scala学习手记28 - Execute Around模式
我们访问资源需要关注对资源的锁定.对资源的申请和释放,还有考虑可能遇到的各种异常.这些事项本身与代码的逻辑操作无关,但我们不能遗漏.也就是说进入方法时获取资源,退出方法时释放资源.这种处理就进入了Ex ...
- java.util.logging.Logger_01
1.参考网址 1.1.java.util.logging.Logger使用详解 http://lavasoft.blog.51cto.com/62575/184492 1.2.Java内置Logger ...
- CSS3 content属性学习
css3中出现了 ":before",":after"伪类, 你可以这样写: h1:after{ content:'h1后插入的文本'; ... } 这两个选择 ...
- MySQL二进制日志功能介绍
二进制日志记录所有更新数据的SQL语句,其中也包含可能更新数据的SQL语句,例如DELETE语句执行过程中无匹配的行.二进制日志中还包含了与执行SQL语句相关的内容,例如SQL语句执行的时间.错误代码 ...