之前开发的项目中,有涉及到UDP和TCP相关的网络编程,但并没有深入去了解。只是简单调用系统函数创建相应的socket,
然后使得两个不同设备进行通讯。而在当时的认知中,只是简单的认为TCP是有连接的,而UDP是无连接的。于是这种认知
在之后带新员工(应届大学生刚毕业)时,被他的一个问题给打破。那新员工问:“TCP协议和JSON协议有什么区别”?
当时我还处在那种无知的“认知”中,我说:“TCP是一种传输协议,而JSON协议则是一种数据组织格式协议”。他不理解。
JSON协议有具体的数据格式可以给他看,但TCP这种他觉得没有任何概念,不懂。于是,开始了我的无知:“TCP是有连接的
,必须要有传输介质,把通讯的两个设备连接起来。”。接着,我还用笔给他画了个示意图:两个设备,中间用了一条线将
这两者连接了起来,然后我说:“瞧,这条线就是TCP传输协议,是数据传输的一条通道”。他似懂非懂的“哦”了一声。
“不是这样的吧,TCP是OSI七层协议中的传输层。数据从一个设备到另一设备的通讯过程中,都会历经这七层协议,每一层
都会在数据端加上相应的协议头,这个协议层就包含了该层所需的信息。如数据链路层,协议头上则会包含源mac地址和目的
mac地址,而传输层则会在数据端加上源端口号和目的端口号,数据就是通过这个目的端口号发到应用层上的”,这时办公室
中一个老员工站了起来,反驳了我(他估计是听得忍不住了。不过他反驳的很好,不然我可能在未来相当长的一段时间内,
继续保持这种无知)。我霎那间,脸烧,尴尬。我对那老员工说:“你来跟他解释这个概念吧”。他过来说了几句,不过新
员工还是茫然不知。然后我对那新员工说:“我过几天再跟你解释下这个概念吧”。于是开始了TCP/IP的学习之路。
其实在这之前,自己是知道OSI七层协议这么个东西的。也知道TCP就是其中的传输层,但其它层是做什么的,一概不知。于
是大脑好像就把这七层协议过滤成只有一层协议:传输。两个设备数据传输,就是靠传输协议去把控的。太天真。这几天学
习了相关的知识,在这做一个总结吧,不知道自己理解对不对,若有人看来这篇文章,希望能够做一下评论,谢谢。
TCP/IP是一个协议簇,包含了很多协议:IP、TCP、UDP、HTTP、FTP等。若将这些进行分层,则可分为四层:从下往上,分别
是网络接口层,网络层,传输层,应用层。网络接口层主要是跟硬件相关的一些协议,如数据接收和发送就是在这一层进行
的。在这一层还有通过MAC地址进行寻址的功能,每个设备都有唯一的MAC地址,这一层也是要通过这个来进行数据的转发。
网络层则是逻辑寻址的一层,如IP协议,在这一层,就是根据IP路由寻址的过程(寻找目标IP所在的子网)。传输层,通过
端口号进行寻址的过程,找到对应的应用程序,每个应用程序都有一个唯一的端口号,应用层和传输层之间数据的交互就是
通过这个端口进行的,所以这一层主要的信息就是端口。当然,如果这一层是TCP协议,这应该还要有相应的功能,如三次
握手,数据包序列号的重新排序再给到应用层等功能。应用层就是数据处理层了。而之前提到的OSI七层协议,则分别是
物理层和数据链路层(这两层对应TCP/IP的网络接口层)、网络层(对应对应TCP/IP的网络层)、传输层(对应TCP/IP的
传输层)、会话层、表示层和应用层(这三层对应TCP/IP的应用层)。
下面附上网上找到的一些关于UDP的一些描述:
发送端口不需要固定什么4000,随机由UdpClient 选择一个发送端口就行。发送之后,立刻读取数据。而
服务器端从端口8000接收到请求,应该立刻向客户端(接收消息时就会获得RemoteIpEndPoint客户端对外
端口)发送返回(比如说200毫秒以内),这样就能让发送消息的客户端收到返回结果。
要注意的是,Udp通讯时你必须较快地让服务器发送返回消息,否则中间的路由器可能已经中断了处理、不
再允许外部主动联系内部了。
这里有必要纠正几个很容易误导人的概念:
1. 客户端不需要固定端口,客户端发送消息时应该由UdpClient自动选择可用的端口。
2. 不要说什么“服务器向4000端口发送消息”,那是瞎扯,是因为只在小办公室里几台电脑规模的小网络
下搞测试造成的。真正应用网络环境下,大部分客户端都在NAT路由器以后,就算是客户端绑定了4000固定
端口,经过1个或者多个NAT路由器处理,那么服务器端针对的客户端端口也不大可能是4000,此时服务器端
可能应该向“最终路由器IP、端口号62102”发送返回消息,才能到达内部局域网IP、4000号端口这里来。
3. 服务器不可能向NAT内部的客户端主动发起消息,必须等客户端发送消息,然后赶紧发送返回值。因为
NAT路由器只会为Udp消息的这种回发规则维护一个较短时间,在这个较短时间上由外部发来的消息才能被
路由器正确转发给内部。
比如说,你的客户端IP是192.168.0.100,端口号是4000,使用Udp方式向服务器 202.100.200.123:8000发
送了一个消息,那么经过路由器——不管中间有几个路由器——最终是以 211.149.101.21:62018 发送出去
的,那么这时候难道有人告诉你说“你的服务器要向 192.168.0.100:4000 发送 Udp 返回值啊!”么?这
就是误导你了。
最起码的Udp服务器端编程,也是向你的服务器端 Receive 语句所返回的 RemoteIpEndPoint(也就是
211.149.101.21:62018 ) 发送返回值,这样才能最终到达局域网内 192.168.0.100:4000。而这个
RemoteIpEndPoint 的值每一次可能都是变动的,因为路由器会采用不同的端口发送不同的消息。
然后,对于客户端,你根本没有必要绑定 4000端口。你的 new UdpClient() 得到的这个对象,可以发送
消息,紧接着Receive,就能收到返回值。发送端口号(不管是4000还是40000)应该由人家自动选择,你
只定一个端口号没有多少好处。
当然这里是指一般的Udp通讯的概念。如果你有什么特别的“设计”,或许你确实需要固定内部端口号。
但是那也是在你确实知道基本的概念之后的事情。