在上一篇中我们已经了解了socket的原形,这节我们来做个实践。顺道把TCP和UDP翻个门清。
TCP:
TCP 是一种面向连接的协议,它为两个计算机之间提供了点到点的可靠数据流,保证从一个端点以正确的顺序无差别的发送到另一个端点,所以说TCP是可靠的数据传输协议。
UDP:
UDP 是面向无连接的协议,它是从一个端点向另一个端点发送数据报的,但是各数据报都是相互独立的。并不能保证数据报能以正确的顺序到达另一个端点。所以它是不可靠的数据传输协议。
通过上面的基本定义,我们简单了解了这两个协议是干啥的。但是在真正的开发中要如何选择使用哪种协议呢?想知道怎么选择,那么我们一定要搞清楚这两者的区别是什么:
在网上看到过一个对于此解释特别贴切的例子:有两座高山,在这两座高山中间有一个迷雾重重的大峡谷,其中A,B各在一座山峰上。而且他们都相互知道对面的山峰有人。有一天B说想吃苹果了,但是他所在的山峰没有,就告诉了A正好A所在的山上特别多,A如果想把这10个苹果给B那么他现在有两种传送模式:
• 首先A拿着这10个苹果来到峡谷旁边开始呼唤B,因为大峡谷迷雾太重互相看不见对方,所以只有等B应答了之后,A才知道B也来到了峡谷旁边,这个时候A开始一个一个的往对面扔:
A:下面我开始扔第一个了
B:仍吧,我在对面呢
A:好,哈嘿~~~
B:好了 , 我接住了
A:好 那我扔第二个了
B:好 仍吧
A:哈嘿~~
B:接到了
A:我开始扔第三个了
B:嗯嗯 快点吧
A:哈嘿~~~
B:·····
A:啥情况?你接到了没有?
B:没有呀 估计你劲用小了,我没有收到
A:没关系,我再扔。哈嘿~~~
B:好 接住了
就这样,A跟B一个接一个的 扔了10个过去。
• A知道B想要10个苹果,所以A直接跑到大峡谷边上,也不呼唤B直接一个接一个的朝对面大峡谷扔了10个苹果过去。至于到时候B能捡到几个,那A就不关心了,反正我扔了10个过去。
对面这两种方法你会发现,在第一种方法中,A先呼唤B,等B应答后才开始扔苹果,这就是连接。在第二种方法中,A并没有去呼唤B,也没有等B的应答,直接就扔了,这就是无连接。从时间层面去分析,你发现第一种方法,从A开始呼唤B到扔完10个苹果,他们可能需要1个小时。而在第二种方法中,从A开始扔苹果到扔完10个苹果,兴许只用了十几秒就搞定了,这就是效率。第一种方法明显要比第二种方法更消耗A的时间和精力。从B角度来说,第一种方法使B能一个一个的收到苹果,一个不少。而第二种方法,B并不一定可以都收到,也许一个也找不到。所以第一种方法的正确抵达率更高。
第一种方法就是TCP,第二种就是UDP
在我们具体开发的时候,要根据自己的实际情况去选择具体该使用哪个传输方法。如果你对效率要求高,那你就使用第二种方法(UDP)。如果你对准确性要求高,那你就选择第一种方法(TCP)。而且需要注意的是,第一种方法只能点对点的传输,但第二种既可以点对点,也可以点对多等等。
端口
端口其实就是对外实现数据交流的窗口。下面还是举例子说明:
比如说,你电脑上安装了QQ和微信。但是你的电脑只有一根网线。那如果你的QQ好友给你发送消息的话,那么在你的电脑从网络层取到数据后是怎么知道该条数据是发给QQ呢?还是发给你的微信呢?这个时候就用到了端口的概念。QQ应用有自己的端口号,微信应用程序也有属于自己的端口号。在你的电脑收到一条消息后,协议一看“哦 这个是QQ的消息”然后你的QQ就开始闪烁了。
但是有个常识,我们一定要记住,端口号的取值范围是0~65535,其中0~1023是保留给预定义服务的,用于一些知名的网络服务和应用。比如:http:80 FTP:21等,我们在开发中一般使用1024之后的端口号。
下面我们写一个简单的网络小应用来帮助理解上节学习的内容,创建一个服务端然后再创建一个客户端,使之之间可以实现简单的通信。下面我的每一行代码都会给你注释,参考一下,容易理解
创建TCP服务器
#引入socket模块和time的ctime模块
from socket import *
from time import ctime
HOST="" #因为是服务端,所以这里我什么也没有写,这表示它可以使用任何可用的地址
PORT=31234 #这是我们随机选的一个端口,1~1024是预留端口,然后随机取一个不被占用的就行
BUFSIZ=1024 #在这里设置缓冲区大小为1KB,后面可以根据你自己的情况去设置
ADDR=(HOST,PORT) #这就是后面我们要绑定的地址了
tcpSerSock=socket(AF_INET,SOCK_STREAM) #创建了一个socket
tcpSerSock.bind(ADDR) #将套接字绑定到地址上
tcpSerSock.listen(5) #开启监听,5的意思是在被转接或者拒绝之前最多可以接受多少个请求
while True:#然后开始等待客户端的请求
print("Waittingforconnection...")
tcpCliSock,addr=tcpSerSock.accept()#accept()是阻塞的,在没有请求进来的情况下,会一直在这里等待
print("...connectedfrom:",addr)#有连接之后我们打一串提醒并把地址给给打印出来
while True:#进入数据交换循环
data=tcpCliSock.recv(BUFSIZ).decode()#接受客户端发送来的数据,
if not data:#如果接受到一个空的消息,我们就认为客户端已退出
break
tcpCliSock.send(("[%s]%s"%(bytes(ctime(),"utf-8"),data)).encode())#如果接受到的数据不是空,那我们就在他前面加上时间戳,然后给他返回去
tcpCliSock.close()#关闭关闭当前客户端的连接
下面创建我们的客户端:
#引入socket模块
from socket import *
HOST="localhost" #这里指定的是服务器的地址,由于我服务端和客户端都是在同一台机器上,所以写了是这个
PORT=31234#端口要和服务端保持一致
BUFSIZ=1024#缓冲区,根据自己的情况来设置
ADDR=(HOST,PORT)
tcpCliSock=socket(AF_INET,SOCK_STREAM)#创建一个socket
tcpCliSock.connect(ADDR)#主动去链接服务端
while True:
data=input("请输入:")
if not data:#如果输入为空 我们就认为他断开了
break
tcpCliSock.send(data.encode())#把输入的数据发送给服务端
data=tcpCliSock.recv(BUFSIZ)#接受服务端的返回
if not data:#如果返回空则退出并关闭链接
break
print(data.decode("utf-8"))#如果不是空就打印出来
tcpCliSock.close()
好的,现在我们客户端和服务端都写好了。由于服务端是等待接入的那个,所以我们先运行它然后运行客户端,线面看一下他们的执行顺序:
执行顺序 | 客户端 | 服务端 |
---|---|---|
1 | 执行后输入:Waitting for connection… | |
2 | 执行后输出:请输入: | |
3 | 执行了客户端后输出:..connected from : (‘127.0.0.1’, 49999) | |
4 | 输入:“hello” | |
5 | 收到并输出信息:“[b’Mon Aug 27 21:41:20 2018’] hello 请输入:” | |
6 | 输入:“你好” | |
7 | 收到并输出信息:“[b’Mon Aug 27 21:42:13 2018’] 你好 请输入:” |
到此我们这个简单的网络应用就算完成了,通过这个应用我们不仅展示了数据是如何从客户端到达服务端并最后又返回客户端的,而且把服务端当做一个时间的服务器,因为我们收到的时间都是来自服务端的。
更多内容请关注“计算机自学平台”