【Python之旅】第五篇(二):Python Socket单线程+阻塞模式

 前面第五篇(一)中的一个Socket例子其实就是单线程的,即Server端一次只能接受来自一个Client端的连接,为了更好的说明socket单线程和阻塞模式,下面对前面的例子做修改。

1.单线程+阻塞+交互式

    前面的例子是单线程阻塞和非交互式的,现在改写为交互式的,即不会执行一次就结束,希望达到的效果是,发送的数据由User输入,然后Server端进行接收。

Server端:与上个例子一样,并没有什么变化


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import socket                #导入socket类

  

HOST =''                     #定义侦听本地地址口(多个IP地址情况下),这里表示侦听所有,也可以写成0.0.0.0

PORT = 50007                 #Server端开放的服务端口

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    #选择Socket类型和Socket数据包类型

s.bind((HOST, PORT))         #绑定IP地址和端口

s.listen(1)                  #定义侦听数开始侦听(实际上并没有效果)

conn, addr = s.accept()      #定义实例,accept()函数的返回值可以看上面的socket函数说明

  

print 'Connected by', addr

while 1:

    data = conn.recv(1024)    #接受套接字的数据

    if not data:break         #如果没有数据接收,则断开连接

    print 'revc:',data        #发送接收到的数据

    conn.sendall(data)        #发送接收到的数据

conn.close()                      #关闭套接字

Client端:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

import socket

 

HOST = '192.168.1.13'

PORT = 50007

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((HOST, PORT))

 

while True:

    user_input = raw_input('msg to send:').strip()    #由User输入要发送的数据

    s.sendall(user_input)

    data = s.recv(1024)

    print 'Received', repr(data)

 

s.close()

演示:

步骤1:Server端运行服务端程序


1

2

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python server4.py 

===>光标在此处处于等待状态

步骤2:Client A端运行客户端程序


1

2

3

4

5

6

7

8

9

10

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python client4.py 

msg to send:The first msg.    ===>User输入数据

Received 'The first msg.'     ===>Server端返回的数据

msg to send:The second msg.

Received 'The second msg.'

msg to send:The third msg.

Received 'The third msg.'

msg to send:I'm A.

Received "I'm A."

msg to send:                  ===>继续等待User输入数据

步骤3:在Server端中观察现象


1

2

3

4

5

6

7

8

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5/[2]sec_4_ver2(单线程,交互式,阻塞模

一般演示)$ python server4.py 

Connected by ('192.168.1.13'52645)

revc: The first msg.    ===>接收到用户发送的数据

revc: The second msg.

revc: The third msg.

revc: I'm A.

===>光标在此处处于等待状态

如果此时有另一个Client B端再连接进来,会有下面的情况:


1

2

3

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python client4.py 

msg to send:I'm B

===>光标在此处处于等待状态

    这时如果在Client A端断开连接,则服务端也会关闭套接字,Client B端发送的数据仍然无法被Server端接收。

    此时服务端即出现阻塞情况,因为服务端还和Client A处于连接状态,无法接收Client B发送的数据,这也说明了此时的Server端是单线程的。

2.单线程+阻塞+交互式的进阶演示

    把上面的例子中的代码再做进一步的修改,以使得阻塞模式的现象更加明显。

Server端:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import socket

 

HOST =''

PORT = 50007

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind((HOST, PORT))

s.listen(1)

 

while 1:

    conn, addr = s.accept()        #在循环中接受Client端连接的请求

    print 'Connected by', addr

    while True:                    #再做一个内部的循环

        data = conn.recv(1024)

        print 'Received',data

        if not data:break

        conn.sendall(data)

conn.close()

Client端:与前面例子的代码一样


1

2

3

4

5

6

7

8

9

10

11

12

13

14

import socket

 

HOST = '192.168.1.13'

PORT = 50007

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((HOST, PORT))

 

while True:

    user_input = raw_input('msg to send:').strip()

    s.sendall(user_input)

    data = s.recv(1024)

    print 'Received', repr(data)

 

s.close()

演示:

步骤1:Server端运行服务端程序


1

2

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python server4.py 

===>光标在此处处于等待状态

步骤2:Client A端运行客户端程序


1

2

3

4

5

6

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python client4.py 

msg to send:Hello!

Received 'Hello!'

msg to send:I'm Client A.

Received "I'm Client A."

msg to send:        ===>继续等待User输入数据

步骤3:在Server端中观察现象


1

2

3

4

5

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python server4.py 

Connected by ('192.168.1.13'52647)

Received Hello!

Received I'm Client A.

===>光标在此处处于等待状态

如果此时有另一个Client B端再连接进来,会有下面的情况:


1

2

3

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python client4.py 

msg to send:I'm Client B.

===>光标在此处处于等待状态

Server端的状态依然为:


1

2

3

4

5

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python server4.py 

Connected by ('192.168.1.13'52647)

Received Hello!

Received I'm Client A.

===>光标在此处处于等待状态

这时试图把Client A端断开:


1

2

3

4

5

6

7

8

9

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python client4.py 

msg to send:Hello!

Received 'Hello!'

msg to send:I'm Client A.

Received "I'm Client A."

msg to send:^CTraceback (most recent call last):

  File "client4.py", line 10in <module>

    user_input = raw_input('msg to send:').strip()

KeyboardInterrupt

再看看Server端的情况:


1

2

3

4

5

6

7

8

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python server4.py 

Connected by ('192.168.1.13'52647)

Received Hello!

Received I'm Client A.

Received 

Connected by ('192.168.1.13'52648)

Received I'm Client B.    ===>成功接收到来自Client B端发送的数据

===>光标在此处处于等待状态

再看看Client B端的情况:


1

2

3

4

xpleaf@xpleaf-machine:/mnt/hgfs/Python/day5$ python client4.py 

msg to send:I'm Client B.

Received "I'm Client B."

msg to send:    ===>光标在此处处于等待状态

    以上的现象,再根据Server端的程序代码,就可以非常好理解单线程模式和阻塞的细节情况了,在这里是这样的:Server端接受Client A端的连接后,即把接受连接的线程释放,但此时仍然占用接收和发送数据的线程,所以Client B端虽然可以连接上Server端,但数据是无法成功被Server端接收的;当Client A端断开与Server端的连接后,Server端的接收和发送数据的线程立即被释放,之后就可以正常接收来自Client B端发送的数据了。

    

    单线程,即数据的串行发送,会导致阻塞,上面的两个例子就非常好地演示了这个阻塞的过程,如果要解决这个问题,当然在Server端就需要支持多线程,即数据折并发。

时间: 2024-08-02 18:44:41

【Python之旅】第五篇(二):Python Socket单线程+阻塞模式的相关文章

【Python之旅】第二篇(五):基于列表、字典和元组的员工信息处理接口

1.基本需求     编写一个查询员工信息表的程序,实现如下功能: (1)让用户输入不小于3个字符查询员工信息 (2)通过员工号或员工个人信息可以精确或模糊查询到员工信息 (3)输出员工信息 2.实现代码与注释    首先提供员工信息的txt文件: 1 2 3 4 xpleaf@xpleaf-machine:/mnt/hgfs/Python/day3$ more student_info.txt  stu1101 mingjia.xu 275896019@qq.com 263 SystemAdm

【Python之旅】第二篇(二):列表与元组

说明:     Python中的列表类似于其它高级语言中的数组,不过Python的列表操作起来要轻松很多.     Python中列表的学习主线主要是围绕对列表参数的操作使用上,重点关注的应该有如下这些: 1 2 3 4 5 6 7 8 9 names.append( names.count( names.extend( names.index( names.insert( names.pop( names.remove( names.reverse( names.sort(     下面的内容

【Python之旅】第一篇:基于文件处理的登陆接口

1.基本需求     编写登陆接口,实现如下需求: (1)输入用户名密码 (2)认证成功后显示欢迎信息 (3)输错三次后锁定 2.实现细节 ·每添加一个用户,需要手动添加三个文件 文件 功能 username_count.txt 记录用户输错密码的次数,最大为3次,如果用户密码输入正确,则重置为0,默认为0 username_lock.txt 记录用户是否被锁定,1表示锁定,0表示未锁定,默认为0 username_passwd.txt 记录用户的密码 ·注:username是指该用户的用户名,

【Python之旅】第二篇(四):字典

说明:     显然Python中字典的学习过程与列表是一样的,主要是围绕下面的函数来进行重点学习: 1 2 3 4 5 6 7 8 9 10 11 >>> xpleaf. xpleaf.clear( xpleaf.copy( xpleaf.get( xpleaf.has_key( xpleaf.items( xpleaf.keys( xpleaf.pop( xpleaf.popitem( xpleaf.setdefault( xpleaf.update( 1.基本操作 --创建一个字典

【Python之旅】第二篇(一):Python文件处理

说明:     主要是file()和open()函数的使用,但在查open()函数的帮助时,会有下面的说明: 1 2 3 >>> help(open) -- Open a file using the file() type, returns a file object.     因此,两个函数其实都是一样的,下面只用file().     在列举file()的作用时,使用help即是很好的方法,下面则是应重点关注的内容: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

【Python之旅】第二篇(三):基于列表处理的购物清单程序

1.基本需求     编写一个购物小程序,要求实现如下功能: (1)让用户输入工资: (2)输出购物菜单及产品价格: (3)计算用户是否可支付: (4)输出用户剩余的钱,问用户是否继续购物,如果选择继续,则继续进行,否则退出程序: (5)若钱不够,输出用户还需要工作多久才能买得起(这里暂不实现此功能). 2.实现基本思路     基本思路可如下所示:     在编写程序的时候即以该思路为主线,具体细节下面再提及. 3.实现细节     基于友好用户界面的原则,实现的细节可总结如下: (1)用户输

【Python之旅】第二篇(九):迭代器

说明:关于Python中迭代器的解释     Iterator是迭代器的意思,它的作用是一次产生一个数据项,直到没有为止.这样在 for 循环中就可以对它进行循环处理了.那么它与一般的序列类型(list, tuple等)有什么区别呢?它一次只返回一个数据项,占用更少的内存.但它需要记住当前的状态,以便返回下一数据项.它是一个有着next()方法的对象.而序列类型则保存了所有的数据项,它们的访问是通过索引进行的.     举个前面的例子来说就像readlines和xreadlines的区别,rea

【Python之旅】第二篇(七):集合

说明: ·类似于数学中学的集合,Python中的集合可以实现去重的功能,通过set()函数来实现: ·sets支持x in set, len(set)和 for x in set: ·作为一个无序的集合,sets 不记录元素位置或者插入点,因此,sets不支持indexing, slicing,或其它类序列(sequence-like)的操作: ·学习集合,主要是学习集合的一系列标准操作:集合创建.集合添加.集合删除.交并差集等: 1.创建集合:set() 1 2 3 4 5 6 7 8 9 1

【Python之旅】第二篇(六):enumerate枚举

1.普通情况下打印列表中索引号及其对应元素     使用下面的循环: 1 2 3 4 5 6 7 8 >>> L = ['a', 'b', 'c', 'd'] >>> for i in L: ...   print L.index(i),i ...  0 a 1 b 2 c 3 d 2.使用enumerate在循环时同时访问索引     可以使用enumerate实现上面的功能: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18