日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

目錄

一、客戶端實現(xiàn)

二、單進程服務(wù)器

2.1 單進程實現(xiàn)

2.2 單進程非阻塞實現(xiàn)

2.3 TCP服務(wù)器(select版)

2.4 epoll版服務(wù)器實現(xiàn)

三、多進程服務(wù)器和多線程服務(wù)器

四、協(xié)程

4.1 協(xié)程的生成器實現(xiàn)

4.2 協(xié)程的greenlet實現(xiàn)

4.3 協(xié)程的gevent實現(xiàn)

4.3.1 gevent的使用

4.3.2 gevent的切換執(zhí)行

4.3.3 gevent的服務(wù)器實現(xiàn)

一、客戶端實現(xiàn)

客戶端比較簡單,并且適用于與不同服務(wù)器通信,代碼如下:

#coding=utf-8

from socket import *

import random

import time

 

serverIp = raw_input("請輸?服務(wù)器的ip:")

connNum = raw_input("請輸?要鏈接服務(wù)器的次數(shù)(例如1000):")

g_socketList = []

for i in range(int(connNum)):

s = socket(AF_.NET, SOCK_STREAM)

s.connect((serverIp, 7788))

g_socketList.Append(s)

print(i)

 

while True:

for s in g_socketList:

s.send(str(random.randint(0,100)))

 

# ?來測試?

#time.sleep(1)

二、單進程服務(wù)器

2.1 單進程實現(xiàn)

linux服務(wù)器開發(fā)學(xué)習(xí)視頻資料,包括Linux,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協(xié)程,DPDK等等,需要知識技術(shù)學(xué)習(xí)視頻文檔資料的朋友可以后臺私信【架構(gòu)】獲取

網(wǎng)絡(luò)編程——服務(wù)器篇

 


網(wǎng)絡(luò)編程——服務(wù)器篇

 

單進程完成一個tcp服務(wù)器,同時只能為一個客戶端服務(wù)。

from socket import *

serSocket = socket(AF_INET, SOCK_STREAM)

# 重復(fù)使?綁定的信息,若服務(wù)器先close,則不用等待2MSL,可以直接綁定下一個客戶端

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

serSocket.listen(5)

while True:

print('-----主進程, , 等待新客戶端的到來------')

newSocket,destAddr = serSocket.accept()

print('-----主進程, , 接下來負責(zé)數(shù)據(jù)處理[%s]-----'%str(destAddr))

try:

while True:

recvData = newSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(destAddr), recvData))

else:

print('[%s]客戶端已經(jīng)關(guān)閉'%str(destAddr))

break

finally:

newSocket.close()

 

serSocket.close()

2.2 單進程非阻塞實現(xiàn)

上面單進程實現(xiàn)同時只能為一個服務(wù)端服務(wù),如果第二個while中不阻塞,則可以實現(xiàn)多用戶同時服務(wù)。代碼如下:

#coding=utf-8

from socket import *

import time

 

# ?來存儲所有的新鏈接的socket

g_socketList = []

 

def main():

serSocket = socket(AF_INET, SOCK_STREAM)

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

 

#可以適當修改listen中的值來看看不同的現(xiàn)象

serSocket.listen(1000)

 

#將套接字設(shè)置為?堵塞

#設(shè)置為?堵塞后, 如果accept時, 恰巧沒有客戶端connect, 那么accept會

#產(chǎn)??個異常, 所以需要try來進?處理

serSocket.setblocking(False)

 

while True:

 

#?來測試

#time.sleep(0.5)

try:

newClientInfo = serSocket.accept()

except Exception as result:

pass

else:

print("?個新的客戶端到來:%s"%str(newClientInfo))

newClientInfo[0].setblocking(False)

g_socketList.append(newClientInfo)

 

# ?來存儲需要刪除的客戶端信息

needDelClientInfoList = []

 

# 為列表中每個客戶端服務(wù)

for clientSocket,clientAddr in g_socketList:

try:

recvData = clientSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(clientAddr), recvData))

else:

print('[%s]客戶端已經(jīng)關(guān)閉'%str(clientAddr))

clientSocket.close()

g_needDelClientInfoList.append((clientSocket,clientAddr))

except Exception as result:

pass

 

for needDelClientInfo in needDelClientInfoList:

g_socketList.remove(needDelClientInfo)

 

if __name__ == '__main__':

main()

2.3 TCP服務(wù)器(select版)

tcp/ip學(xué)習(xí)資料獲取后臺私信【tcp/ip】

網(wǎng)絡(luò)編程——服務(wù)器篇

 

在非阻塞版本中使用for循環(huán)為列表中的每個客戶端服務(wù),而select版是通過調(diào)用select函數(shù)直接返回列表中接收到數(shù)據(jù)的socket,不必循環(huán)遍歷。

優(yōu)點:幾乎所有平臺都支持,有良好的跨平臺性。

缺點:select的?個缺點在于單個進程能夠監(jiān)視的?件描述符的數(shù)量存在最?限制,在Linux上?般為1024, 可以通過修改宏定義甚?重新編譯內(nèi)核的?式提升這?限制, 但是這樣也會造成效率的降低。

?般來說這個數(shù)? 和系統(tǒng)內(nèi)存關(guān)系很?, 具體數(shù)? 可以cat /proc/sys/fs/filemax查看。 32位機默認是1024個。 64位機默認是2048.個。對socket進?掃描時是依次掃描的, 即采?輪詢的?法, 效率較低。

當套接字?較多的時候, 每次select()都要通過遍歷FD_SETSIZE個Socket來完成調(diào)度, 不管哪個Socket是活躍的, 都遍歷?遍。 這會浪費很多CPU時間。

select函數(shù)解釋如圖:

網(wǎng)絡(luò)編程——服務(wù)器篇

 

select版tcp服務(wù)器代碼如下:

import select

import socket

import sys

 

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

server.bind(('', 7788))

server.listen(5)

 

inputs = [server, sys.stdin]

 

running = True

 

while True:

 

# 調(diào)用 select 函數(shù),阻塞等待

readable, writeable, exceptional = select.select(inputs, [], [])

 

# 數(shù)據(jù)抵達,循環(huán)

for sock in readable:

 

# 監(jiān)聽到有新的連接

if sock == server:

conn, addr = server.accept()

# select 監(jiān)聽的socket

inputs.append(conn)

 

# 監(jiān)聽到鍵盤有輸入

elif sock == sys.stdin:

cmd = sys.stdin.readline()

running = False

break

 

# 有數(shù)據(jù)到達

else:

# 讀取客戶端連接發(fā)送的數(shù)據(jù)

data = sock.recv(1024)

if data:

sock.send(data)

else:

# 移除select監(jiān)聽的socket

inputs.remove(sock)

sock.close()

 

# 如果檢測到用戶輸入敲擊鍵盤,那么就退出

if not running:

break

 

server.close()

2.4 epoll版服務(wù)器實現(xiàn)

為了解決select版并發(fā)連接數(shù)目的限制,出現(xiàn)了poll版,與select版幾乎相同,唯一不同的是數(shù)量不受限制,仍是用的輪詢方式。后來為了解決poll版輪詢監(jiān)測方式低下的問題出現(xiàn)了epoll版,epoll版相當于“有問題舉手”,而不是“挨個問是否有問題”。

epoll版的優(yōu)點:

1. 沒有最?并發(fā)連接的限制, 能打開的FD(指的是?件描述符), 通俗的理解就是套接字對應(yīng)的數(shù)字編號)的上限遠?于1024

2. 效率提升, 不是輪詢的?式, 不會隨著FD數(shù)?的增加效率下降。 只有活躍可?的FD才會調(diào)?callback函數(shù); 即epoll最?的優(yōu)點就在于它只管你“活躍”的連接, ?跟連接總數(shù)?關(guān), 因此在實際的?絡(luò)環(huán)境中, epoll的效率就會遠遠?于select和poll。

epoll版tcp服務(wù)器代碼如下:

代碼解釋:

epoll的三種事件:

EPOLLIN (可讀)

EPOLLOUT (可寫)

EPOLLET (ET模式)

epoll對?件描述符的操作有兩種模式: LT(level trigger 水平觸發(fā)) 和ET(edge trigger 邊沿觸發(fā)) 。 LT模式是默認模式, LT模式與ET模式的區(qū)別如下:

LT模式: 當epoll檢測到描述符事件發(fā)?并將此事件通知應(yīng)?程序, 應(yīng)?程序可以不?即處理該事件

ET模式: 當epoll檢測到描述符事件發(fā)?并將此事件通知應(yīng)?程序, 應(yīng)?程序必須?即處理該事件,否則會丟失

import socket

import select

 

# 創(chuàng)建套接字

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

 

# 設(shè)置可以重復(fù)使?綁定的信息

s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

 

# 綁定本機信息

s.bind(("",7788))

 

# 變?yōu)楸粍?/p>

s.listen(10)

 

# 創(chuàng)建?個epoll對象

epoll=select.epoll()

 

# 測試, ?來打印套接字對應(yīng)的?件描述符

# print s.fileno()

# print select.EPOLLIN|select.EPOLLET

 

# 注冊事件到epoll中

# epoll.register(fd, [eventmask])

# 注意, 如果fd已經(jīng)注冊過, 則會發(fā)?異常

# 將創(chuàng)建的socket添加到epoll的事件監(jiān)聽中

# [eventmask]為監(jiān)聽的事件列表,有列表中的事件時才會放入epoll列表,

# 事件有三種,EPOLLIN接收數(shù)據(jù)事件,EPOLLOUT發(fā)送數(shù)據(jù),EPOLLET模式(水平觸發(fā)或邊沿觸發(fā))

epoll.register(s.fileno(),select.EPOLLIN|select.EPOLLET)

 

# connections用于存儲socket,addresses用于存儲端口,

# 它們都為字典,key為socket的文件描述符,value為socket或端口!

connections = {}

addresses = {}

 

# 循環(huán)等待客戶端的到來或者對?發(fā)送數(shù)據(jù)

while True:

 

# epoll 進? fd 掃描的地? -- 未指定超時時間則為阻塞等待

# 等價于select版本中的 readable,xxx,yyy = select([],[],[])

# 不為輪詢,使用的是事件通知機制,為本代碼的核心

epoll_list=epoll.poll()

 

# 對事件進?判斷

for fd,events in epoll_list:

# print fd

# print events

 

# 如果是socket創(chuàng)建的套接字被激活

if fd == s.fileno():

conn,addr=s.accept()

 

print('有新的客戶端到來%s'%str(addr))

 

# 將 conn 和 addr 信息分別保存起來

# 注意connections和address為字典,以key、value存儲

connections[conn.fileno()] = conn

addresses[conn.fileno()] = addr

 

# 向 epoll 中注冊 連接 socket 的 可讀 事件

epoll.register(conn.fileno(), select.EPOLLIN | select.EPOLLET)

 

elif events == select.EPOLLIN:

# 從激活 fd 上接收

recvData = connections[fd].recv(1024)

 

if len(recvData)>0:

print('recv:%s'%recvData)

else:

# 從 epoll 中移除該 連接 fd

epoll.unregister(fd)

 

# server 則主動關(guān)閉該 連接 fd

connections[fd].close()

 

print("%s---offline---"%str(addresses[fd]))

三、多進程服務(wù)器和多線程服務(wù)器

代碼說明:

1、多進程實現(xiàn)和多線程實現(xiàn)幾乎相同,不同點:1、創(chuàng)建的時候;2、while中多進程實現(xiàn)中,由于子進程復(fù)制了一份,所以可以關(guān)閉,多線程中,子線程之間共享資源,所以在while中不能關(guān)閉。

2、代碼中使用try...finally,目的是可以使用Ctrl+C強制結(jié)束進程或線程。

多進程服務(wù)器代碼如下:

#coding=utf-8

from socket import *

from multiprocessing import *

from time import sleep

 

# 處理客戶端的請求并為其服務(wù)

def dealWithClient(newSocket,destAddr):

while True:

recvData = newSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(destAddr), recvData))

else:

print('[%s]客戶端已經(jīng)關(guān)閉'%str(destAddr))

break

newSocket.close()

 

def main():

serSocket = socket(AF_INET, SOCK_STREAM)

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

serSocket.listen(5)

 

try:

while True:

print('-----主進程,,等待新客戶端的到來------')

newSocket,destAddr = serSocket.accept()

print('-----主進程,,接下來創(chuàng)建?個新的進程負責(zé)數(shù)據(jù)處理------')

client = Process(target=dealWithClient, args=(newSocket,destAddr))

client.start()

 

#因為已經(jīng)向?進程中copy了?份(引?),并且?進程中這個套接字所以關(guān)閉

newSocket.close()

finally:

#當為所有的客戶端服務(wù)完之后再進?關(guān)閉,表示不再接收新的客戶端的鏈接

serSocket.close()

 

if __name__ == '__main__':

main()

多線程服務(wù)器代碼如下:

#coding=utf-8

from socket import *

from threading import Thread

from time import sleep

 

# 處理客戶端的請求并執(zhí)?事情

def dealWithClient(newSocket,destAddr):

while True:

recvData = newSocket.recv(1024)

if len(recvData)>0:

print('recv[%s]:%s'%(str(destAddr), recvData))

else:

print('[%s]客戶端已經(jīng)關(guān)閉'%str(destAddr))

break

 

newSocket.close()

 

def main():

serSocket = socket(AF_INET, SOCK_STREAM)

serSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR , 1)

localAddr = ('', 7788)

serSocket.bind(localAddr)

serSocket.listen(5)

try:

while True:

print('-----主進程, , 等待新客戶端的到來------')

newSocket,destAddr = serSocket.accept()

 

print('-----主進程, , 接下來創(chuàng)建?個新的進程負責(zé)數(shù)據(jù)處理[%s]-----')

client = Thread(target=dealWithClient, args=(newSocket,destAddr))

client.start()

 

#因為線程中共享這個套接字, 如果關(guān)閉了會導(dǎo)致這個套接字不可?,

#但是此時在線程中這個套接字可能還在收數(shù)據(jù), 因此不能關(guān)閉

#newSocket.close()

finally:

serSocket.close()

 

if __name__ == '__main__':

main()

四、協(xié)程

協(xié)程學(xué)習(xí)資后臺私信【協(xié)程】獲取文檔代碼

網(wǎng)絡(luò)編程——服務(wù)器篇

 

進程里面有線程,線程里面有協(xié)程。協(xié)程不牽扯到切換,并且能完成多任務(wù)。

注意:計算密集型時用多進程;IO密集型使用多線程、多協(xié)程。

通俗的理解: 在?個線程中的某個函數(shù), 可以在任何地?保存當前函數(shù)的?些臨時變量等信息, 然后切換到另外?個函數(shù)中執(zhí)?, 注意不是通過調(diào)?函數(shù)的?式做到的, 并且切換的次數(shù)以及什么時候再切換到原來的函數(shù)都由開發(fā)者??確定。

協(xié)程和線程的區(qū)別:線程切換從系統(tǒng)層?遠不?保存和恢復(fù) CPU上下?這么簡單。 操作系統(tǒng)為了程序運營的?效性每個線程都有??緩存Cache等等數(shù)據(jù), 操作系統(tǒng)還會幫你做這些數(shù)據(jù)的恢復(fù)操作。所以線程的切換?常耗性能。 但是協(xié)程的切換只是單純的操作CPU的上下?, 所以?秒鐘切換個上百萬次系統(tǒng)都抗的住。

協(xié)程調(diào)度:操作系統(tǒng)不感知協(xié)程,所以操作系統(tǒng)不會對協(xié)程調(diào)度。 ?前的協(xié)程框架?般都是設(shè)計成 1:N 模式。 所謂 1:N 就是?個線程作為?個容器??放置多個協(xié)程。 那么誰來適時的切換這些協(xié)程? 答案是有協(xié)程自己主動讓出CPU, 也就是每個協(xié)程池??有?個調(diào)度器, 這個調(diào)度器是被動調(diào)度的。 意思就是他不會主動調(diào)度。 ?且當?個協(xié)程發(fā)現(xiàn)自己執(zhí)行不下去了(比如異步等待?絡(luò)的數(shù)據(jù)回來, 但是當前還沒有數(shù)據(jù)到), 這個時候就可以由這個協(xié)程通知調(diào)度器, 這個時候執(zhí)?到調(diào)度器的代碼, 調(diào)度器根據(jù)事先設(shè)計好的調(diào)度算法找到當前最需要CPU的協(xié)程。 切換這個協(xié)程的CPU上下?把CPU的運?權(quán)交給這個協(xié)程, 直到這個協(xié)程出現(xiàn)執(zhí)行不下去需要等等的情況, 或

者它調(diào)?主動讓出CPU的API之類, 觸發(fā)下?次調(diào)度。

協(xié)程調(diào)度存在問題:假設(shè)一個線程中有?個協(xié)程是CPU密集型的他沒有IO操作, 也就是自己不會主動觸發(fā)調(diào)度器調(diào)度的過程, 那么就會出現(xiàn)其他協(xié)程得不到執(zhí)?的情況, 所以這種情況下需要程序員? ?避免。 這是?個問題, 假設(shè)業(yè)務(wù)開發(fā)的?員并不懂這個原理的話就可能會出現(xiàn)問題。

協(xié)程的優(yōu)點:在IO密集型的程序中由于IO操作遠遠慢于CPU的操作, 所以往往需要CPU去等IO操作。 同步IO下系統(tǒng)需要切換線程, 讓操作系統(tǒng)可以在IO過程中執(zhí)?其他的東?。 這樣雖然代碼是符合?類的思維習(xí)慣但是由于?量的線程切換帶來了?量的性能的浪費, 尤其是IO密集型的程序。

所以?們發(fā)明了異步IO。 就是當數(shù)據(jù)到達的時候觸發(fā)我的回調(diào)。 來減少線程切換帶來性能損失。 但是這樣的壞處也是很?的, 主要的壞處就是操作被“分片” 了, 代碼寫的不是 “一氣呵成” 這種。 而是每次來段數(shù)據(jù)就要判斷 數(shù)據(jù)夠不夠處理, 夠處理就處理, 不夠處理就再等等。 這樣代碼的可讀性很低, 其實也不符合?類的習(xí)慣。

但是協(xié)程可以很好解決這個問題。 比如 把?個IO操作 寫成?個協(xié)程。 當觸發(fā)IO操作的時候就自動讓出CPU給其他協(xié)程。 要知道協(xié)程的切換很輕的。 協(xié)程通過這種對異步IO的封裝 既保留了性能也保證了代碼的容易編寫和可讀性。在?IO密集型的程序下很好。 但是高CPU密集型的程序下沒啥好處。

4.1 協(xié)程的生成器實現(xiàn)

協(xié)程使用生成器來實現(xiàn)的,代碼如下(只切換了函數(shù)調(diào)用,所以效率比較高):

import time

 

def A():

while True:

print("----A---")

yield

time.sleep(0.5)

 

def B(c):

while True:

print("----B---")

c.next()

time.sleep(0.5)

 

if __name__=='__main__':

a = A()

B(a)

結(jié)果如下:

--B--

--A--

--B--

--A--

--B--

--A--

--B--

--A--

...省略...

4.2 協(xié)程的greenlet實現(xiàn)

與生成器實現(xiàn)類似。

注意:進程、線程的調(diào)用是操作系統(tǒng)決定的,執(zhí)行順序不可預(yù)測,而協(xié)程是程序員決定的執(zhí)行順序可預(yù)測,這由以下代碼可知(當執(zhí)行到xx.switch()時會切換)。

使用下面命令安裝greenlet:

sudo pip install greenlet #Python2的安裝方式

sudo pip3 install greenlet #python3的安裝方式

協(xié)程的greenlet實現(xiàn)代碼如下:

#coding=utf-8

 

from greenlet import greenlet

import time

 

def test1():

while True:

print "---A--"

gr2.switch() # 切換到gr2(即test2)中執(zhí)行,test2執(zhí)行切換時會從當前接著執(zhí)行

time.sleep(0.5)

 

def test2():

while True:

print "---B--"

gr1.switch() # 切換到gr1(即test1)中執(zhí)行,test1執(zhí)行切換時會從當前接著執(zhí)行

time.sleep(0.5)

 

gr1 = greenlet(test1)

gr2 = greenlet(test2)

 

#切換到gr1(即test1函數(shù))中執(zhí)行

gr1.switch()

結(jié)果如下:

--A--

--B--

--A--

--B--

--A--

--B--

--A--

...省略...

4.3 協(xié)程的gevent實現(xiàn)

gevent是對greenlet的再次封裝,不用程序員自己編程切換,當遇到需要切換的地方會自動切換。

4.3.1 gevent的使用

#coding=utf-8

#請使?python 2 來執(zhí)?此程序

import gevent

 

def f(n):

for i in range(n):

print gevent.getcurrent(), i

 

g1 = gevent.spawn(f, 5) # 綁定f函數(shù),執(zhí)行5次

g2 = gevent.spawn(f, 5)

g3 = gevent.spawn(f, 5)

 

# 清除協(xié)程

g1.join()

g2.join()

g3.join()

執(zhí)行結(jié)果:

一瞬間執(zhí)行完畢,g1、g2、g3依次順序執(zhí)行,并非交替執(zhí)行,不是我們想要的結(jié)果。

<Greenlet at 0x10e49f550: f(5)> 0

<Greenlet at 0x10e49f550: f(5)> 1

<Greenlet at 0x10e49f550: f(5)> 2

<Greenlet at 0x10e49f550: f(5)> 3

<Greenlet at 0x10e49f550: f(5)> 4

<Greenlet at 0x10e49f910: f(5)> 0

<Greenlet at 0x10e49f910: f(5)> 1

<Greenlet at 0x10e49f910: f(5)> 2

<Greenlet at 0x10e49f910: f(5)> 3

<Greenlet at 0x10e49f910: f(5)> 4

<Greenlet at 0x10e49f4b0: f(5)> 0

<Greenlet at 0x10e49f4b0: f(5)> 1

<Greenlet at 0x10e49f4b0: f(5)> 2

<Greenlet at 0x10e49f4b0: f(5)> 3

<Greenlet at 0x10e49f4b0: f(5)> 4

4.3.2 gevent的切換執(zhí)行

上面順序執(zhí)行的原因是在f函數(shù)中沒有調(diào)用延時,所以不會切換。gevent當遇到耗時操作時才會切換,所以增加一個延時函數(shù)使它能夠切換,代碼如下:

import gevent

 

def f(n):

for i in range(n):

print gevent.getcurrent(), i

 

#?來模擬?個耗時操作, 注意不是time模塊中的sleep

gevent.sleep(1)

 

g1 = gevent.spawn(f, 5)

g2 = gevent.spawn(f, 5)

g3 = gevent.spawn(f, 5)

 

g1.join()

g2.join()

g3.join()

結(jié)果如下:

<Greenlet at 0x7fa70ffa1c30: f(5)> 0

<Greenlet at 0x7fa70ffa1870: f(5)> 0

<Greenlet at 0x7fa70ffa1eb0: f(5)> 0

<Greenlet at 0x7fa70ffa1c30: f(5)> 1

<Greenlet at 0x7fa70ffa1870: f(5)> 1

<Greenlet at 0x7fa70ffa1eb0: f(5)> 1

<Greenlet at 0x7fa70ffa1c30: f(5)> 2

<Greenlet at 0x7fa70ffa1870: f(5)> 2

<Greenlet at 0x7fa70ffa1eb0: f(5)> 2

<Greenlet at 0x7fa70ffa1c30: f(5)> 3

<Greenlet at 0x7fa70ffa1870: f(5)> 3

<Greenlet at 0x7fa70ffa1eb0: f(5)> 3

<Greenlet at 0x7fa70ffa1c30: f(5)> 4

<Greenlet at 0x7fa70ffa1870: f(5)> 4

<Greenlet at 0x7fa70ffa1eb0: f(5)> 4

4.3.3 gevent的服務(wù)器實現(xiàn)

注意:要使用gevent實現(xiàn)服務(wù)器,不能使用默認的socket,而是使用gevent自己的socket,gevent將常用的耗時操作都重寫了一遍,用于檢測是否為耗時操作。

import sys

import time

import gevent

 

from gevent import socket,monkey

 

# 此語句會將本代碼改寫,位于編譯器級的,具體不清楚!(python為動態(tài)語言在執(zhí)行中可以修改)

# 必須使用!!!

monkey.patch_all()

 

def handle_request(conn):

while True:

#--------------#1處#-----------------

data = conn.recv(1024) # 這是gevent中的recv,為耗時操作,會切換到2處!

if not data:

conn.close()

break

print("recv:", data)

conn.send(data)

 

def server(port):

s = socket.socket()

s.bind(('', port))

s.listen(5)

while True:

#--------------#2處#-----------------

cli, addr = s.accept() # 這是gevent中的accept,為耗時操作,會進行切換!

# 注意:第一次到這里時只有一個協(xié)程,不需要切換,在此等待!

# 不為第一次時切換到1處!

gevent.spawn(handle_request, cli)

 

if __name__ == '__main__':

server(7788)

 

分享到:
標簽:網(wǎng)絡(luò)編程
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定