【转自: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协议主要有以下特点:

  1. 是面向连接的传输层协议
  2. 每个TCP连接只能有两个端点,且只能一对一通信
  3. 通过TCP连接传送数据能够保证报文的完整和准确性
  4. 数据只能够以字节流的形式传输
  5. 传输的数据无消息边界

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的更多相关文章

  1. C# Socket编程(5)使用TCP Socket

    TCP 协议(Transmission Control Protocol,传输控制协议)是TCP/IP体系中面向连接(connection oriented)的传输层(transport layer) ...

  2. Windows下C语言的Socket编程例子(TCP和UDP)

    原文:Windows下C语言的Socket编程例子(TCP和UDP) 刚刚学windows编程,所以想写学习笔记,这是一个简单的Socket程序例子,开发环境是vc6: 首先是TCP server端: ...

  3. 《java入门第一季》之Socket编程通信和TCP协议通信图解

    Socket编程通信图解原理: TCP协议通信图解

  4. Socket编程实践(6) --TCP服务端注意事项

    僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...

  5. Windows Socket编程精华《TCP通信服务器》

    1.网络中进程之间如何通信? 首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的.其实TCP/IP协议族已经帮我们解决了这个问 ...

  6. python socket 编程之一:编写socket的基本步骤

    一.socket 编写server的步骤: 1.第一步是创建socket对象.调用socket构造函数.如: socket = socket.socket( family, type ) family ...

  7. Socket编程基础——面向连接TCP

    WinSock是Windows环境下的网络编程接口,它最初是基于Unix环境下的BSD Socket,是一个与网络协议无关的编程接口.WinSock包含两个主要版本,即WinSock1和WinSock ...

  8. Socket 编程中,TCP 流的结束标志与粘包问题

    因为 TCP 本身是无边界的协议,因此它并没有结束标志,也无法分包. socket和文件不一样,从文件中读,读到末尾就到达流的结尾了,所以会返回-1或null,循环结束,但是socket是连接两个主机 ...

  9. Socket编程:UDP和TCP概论及案例

    网络编程的三要素: 1.IP地址  2.端口 3.协议 什么是Socket? Socket就是通信链路的端点称"套接词". 基于TCP协议的Socket网络通信: 用来实现双向安全 ...

随机推荐

  1. GIT如何使用:大杀器!所有常用指令整理

    1 pwd 显示当前目录2 mkdir 创建目录 cd 进入文件3 git init 变成Git可以管理的仓库(千万不要修改目录下的.git隐藏文件夹)4 ls -ah 可以把.git文件显示出来5 ...

  2. PC平台逆向破解

    ---恢复内容开始--- PC平台逆向破解 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. ...

  3. Linux下的sed命令使用详解

    sed是stream editor的简称,也就是流编辑器.它一次处理一行内容,处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”pattern space,接着用sed命令处理缓冲区中的内容, ...

  4. Percona 工具包 pt-online-schema-change 简介

    mysql的在线表结构修改,因为低效和阻塞读写.一直被诟病.至于ALTER TABLE 的原理,参看我上一篇文章.MySQL在线修改大表结构.看完后,发现的问题是还是会锁的,且对于在线更新的这块也是不 ...

  5. java中string与byte[]之间的转化分析

    背景:最近接触zookeeper的java开发,由于zookeeper中传的好像都是byte[]的数据(需要进一步确认),好多情况下都需要进行转换. 1)和zookeeper原生API不同,通过zkc ...

  6. 什么是webhook

    什么是webhook 翻译,原文地址:https://sendgrid.com/blog/webhook-vs-api-whats-difference/ 一.概述 Webhook是一个API概念,并 ...

  7. 64位机的pl/sql不安装32位oracle的连接方式

    第一步:下载即时客户端包    在Oralce官方网站上下载Oracle Instantclient Basic package.地址如下:http://www.oracle.com/technetw ...

  8. 把本地jar包发布到maven私服和本地maven库

    有时时候下载了jar包,但发现maven库里没有,可以将jar包上传到本地私服和本地maven库: 1.上传到本地私服 mvn deploy:deploy-file -Dfile=D:\GETUI_S ...

  9. scala学习手记9 - =和==

    = 赋值运算 scala的赋值运算和java的有着很大的不同.如a=b这样的赋值运算,在Java中返回值是a的值,在scala中返回的则是Unit(Unit是值类型,全局只存在唯一的值,即(),通常U ...

  10. 今夜我们一起学习 Apache Shiro

    简介 Apache Shiro 是一个功能强大但又非常容易使用的 Java 安全框架,提供了认证,授权,加密以及会话管理功能.因为 Shiro 的 API 是非常容易理解的,所以使用 Shiro 你可 ...