【卷二】网络二—TCP服务器与客户端

经过上回简单地介绍,大家对服务器多少应该清楚一些了吧!还记得TCP: (Transmission Control Protocol)

传输控制协议? 还记得IP: (Internet Protocol)因特网协议, IPv4, IPv6? 如有不清楚,还请回去补充下概念......

传送门: http://www.cnblogs.com/Ruby517/p/5808224.html

参考: 《Python核心编程(3rd)》 P51 ~ P57

-----------------------------------------------------------------------------------------------------------------

 

名称                                  描述                            
                                                
服务器套接字方法                                                
s.bind()                             将地址(主机名,端口号对)绑定到套接字上                            
s.listen()                            设置并启动TCP监听器                            
s.accept()                          被动接受TCP客户端连接,一直等待直到连接到达(阻塞)                            
                                                
客户端套接字方法                                                
s.connect()                        主动发起TCP服务器连接                            
                                                
普通的套接字方法                                                
s.recv()                             接收TCP消息                            
s.send()                            发送TCP消息     

                       

创建TCP服务器

TCP是控制面向连接的套接字的协议,通常和IP一起用,搭建这样一个类型的服务器有何妙用呢?其实我也不知道!

下面是创建TCP服务器的一般伪代码(就是掺杂了很多中文的代码), 由于只是个框架,所以直接按执行只会报错 !

 1 # coding: utf-8
 2 
 3 from socket import *             # 导入socket模块的所有特性
 4 
 5 ss = socket()                    # ss: (server socket)服务器表接字,创接服务器表接字
 6 ss.bind()                        # 套接字与本地地址绑定
 7 ss.listen()                      # 开启TCP监听器
 8 
 9 while True:                      # 服务器无限循环
10     cs = ss.accept()             # cs: (client-side)客户端, 被动接受客户端连接
11     while True:                  # 通信循环
12         cs.recv()/cs.send()      # 对话 (接收/发送)
13     cs.close()                   # 关闭客户端套接字
14 ss.close()                       # 关闭服务器套接字
点我

        所有套接字都是通过使用socket.socket()函数来创建的。因为服务器需要占用一个端口并等待

客户端的请求, 所以它们必须绑定一个本地地址。因为TCP是一种面向连接的通信系统, 所以在TCP

服务器开始之前,必须安装一些基础设施。特别地,TCP服务器必须监听传入的连接。

        默认情况下,accept()是闭塞的,意味着执行将会停止,直到一个连接到达。这也就是说,服务

器只能无可奈何地等待它的顾客,客户端联系它。

        一旦服务器接受了一个连接,就会返回(利用accept())一个独立的客户端套接字,用来与即将

到来的消息进行交换。使用新的套接字类似于接线员将客户的电话切换给客服代表。当一个客户电话最

后接进来时,主要的总机接线员会接到这个电话,并使用另一条线路将这个电话转接给合适的人  (如客

服) 来处理客户的需求。

        这将能够空出主线(旧的,原始的套接字),以便接线员可以继续等待新的电话(客户请求),

而此时客户及其连接的客服代表能够进行他们自己的谈话。同样地,当一个传入的请求到达时, 服务

器会创建一个新的通信端口来直接来客户端进行通信,再次空出主要端口, 以使其能够接受新的客户

端连接!

 

TCP时间戳服务器

【时间戳指的是ctime()输出的内容】

注意,格式化字符串%s无法传递元组,所以要先用str()函数将元组转化为字符串

 1 # coding: utf-8
 2 
 3 from socket import *
 4 from time import ctime
 5 
 6 # Host: 主机, Host变量是空白的,表示它可以使用任何可用的地址 
 7 # Port: 端口, 端口号范围为0~65535 (尽管小于1024的端口号都预留给了系统)
 8 # Buffer Size: 缓冲区大小, 这里设置为1024B, 即1KB
 9 # listen()方法/函数的参数是在连接被转接或拒绝之前,请求的最多次数
10 
11 Host = ""                                              
12 Port = 21568                   
13 Bufsiz = 1024                  
14 Addr = (Host, Port)
15 
16 tcpSerSock = socket(AF_INET, SOCK_STREAM)
17 tcpSerSock.bind(Addr)
18 tcpSerSock.listen(5)
19 
20 while True:
21     print "Waiting for connection..."
22     tcpCliSock, addr = tcpSerSock.accept()
23     # %s 可以代替str(字符串) int(整数)、float(浮点数)、list(列表)、dict(字典), 就是不能代替tuple(元组)
24     print "...Connected from: %s" % str(addr)
25 
26     while True:
27         # 从Buffer: 缓冲区, 取出客户端发来的数据data进行加工
28         data = tcpCliSock.recv(Bufsiz)
29         # 如果data是空白值,那么它的布尔值即为假,not data即为真,那么执行break语句,跳出循环
30         if not data:
31             break
32         # ctime()表示当前的系统时间,即时间戳, 再把处理好的数据发回缓冲区,供客户端利用
33         tcpCliSock.send("[%s] %s" % (ctime(), data)) 
34         # 关闭客户端套接字
35     tcpCliSock.close()
36 
37 tcpSerSock.close()
点我

        一旦进入到服务器的无限循环中,我们就只有被动等待客户端地连接。当一个连接请求出现时,

我们进入到对话循环中(第二个while语句范围), 在该循环中等待客户端发送的信息。 如果信息是空

白的, 这意味着客户端已经退出,所以此时将跳出对话循环,关闭当前客户端连接,然后等待另一个

客户端连接 (只退出第二个while对话循环,第一个服务器循环还在运行)!

        错误1, 如果出现这种报错, 说明port: 端口号已经被其他程序占用, 也许你已经打开过的服

务器没有关掉就又新打开一个了!  此时关掉开着的服务器或者换一个端口号就行了。 (有效的端口号

范围是0~65535,小于1024的端口号预留给了系统!)

查看端口使用:  >-|->> http://jingyan.baidu.com/article/3c48dd34491d47e10be358b8.html

         错误2, bad  descriptor, 一定是因为你把客户端的结束语句放在对话循环里面了(第二个while语句范围)

         单独运行服务器的话,由于此时尚没有客户端的请求,服务器输出的只有:

 

----------------------------------------------------------------------------------------------------------------

 TCP时间戳客户端

【注意】 由于客户端的connect()函数是主动发起连接的,所以若是先启动客户端而不是服务器的话,

没有服务器的响应,那么将会出现如下错误:

 1 # coding: utf-8
 2 
 3 from socket import *
 4 
 5 # Host:主机, Port:端口, 指服务器的主机名和端口号, 因为在同一个计算机进行测试(本例中)
 6 # 所以Host包含本地主机名 (如果你的服务器运行在另一台主机上,那么需要进行相应修改)。端
 7 # 口号Port 应该与服务器设置的完全相同(否则将无法进行通信)!
 8 
 9 Host = "localhost"
10 Port = 21568
11 Bufsiz = 1024
12 Addr = (Host, Port)
13 
14 tcpCliSock = socket(AF_INET, SOCK_STREAM)
15 tcpCliSock.connect(Addr)
16 
17 while True:
18     data = raw_input("> ")
19     # 如果data是空白值,那么BOOL(data)布尔值将会是False,not data即是True,执行break语
20     # 句,跳出while循环
21     if not data:
22         break
23     # 把数据data发送到Buffer(缓冲区), 供服务器提取, 进行加工
24     tcpCliSock.send(data)
25     # 把服务器处理好的数据从Buffer(缓冲区), 取出来, 并打印出来!
26     data = tcpCliSock.recv(Bufsiz)
27     
28     print data
展开

客户端的主机名和端口号必须与服务器的一致,否则,将无法进行通信!  因为此处是在同一台计算机上进行测

试,所以在Host包含本地主机名(localhost), 如果你的服务器运行在另一台主机上,那么需要进行相应地修改。

如果出现这种错误,说明服务器和客户端没有连接上,可能是还没启动服务器就先启动客户端了,也可能是两者的

端口或地址对不上 (比如忘记写本地主机名称'localhost' !!!)

客户端的输出,可以看出,它先把自行输入的数据发送到缓冲区,然后服务器将数据从缓冲区取出来加工 (还是

通过服务器代码里的tcpCliSock.recv()方法),最后输出的是经过服务器处理过的数据,即我们输入的数据加

上时间戳!

服务器的输出,主要是诊断性的,当客户端发起连接时, 将会收到"...Connected from..."的消息。 当继续接收

"服务"时,服务器会等待新客户端的连接!

【127.0.0.1是回送地址,指本地机,一向用来测试使用!】

-------------------------------------------------------------------------------------------------------------

客户端改进

上面的代码有个问题,就是当服务器与客户端还是通话的时候,我们若是按 "Ctrl + C" 引发服务器异常从而关闭,

那么此时客户端的显示是这样的,输出空白,个人猜测是由于服务器在执行tcpSerSock.recv()时引发异常,但两

者好歹对话过,所以系统返回了空白符给客户端,

可是第二次就出错了,因为此时服务器已关,也就是没有TCP连接了,所以也就没有了交互,系统怎么返回信息给

你所以这回连空白符都没有(纯属个人猜测)

所以我们要在客户端代码的 tcpCliSock.recv() 后面这么改...

1 data = tcpCliSock.recv(BUFSIZ)
2 
3 if not data:
4     break
5 
6 print data

这样就可以在引发服务器异常,然后客户端收到空白符时直接退出客户端

 

posted @ 2016-08-26 20:50  坏小孩D_R  阅读(420)  评论(0编辑  收藏  举报