socket編程瞭解

Socket 編程html

Socket通信原理描述:python

套接字是爲特定網絡協議(例如TCP/IP,ICMP/IP,UDP/IP等)套件對上的網絡應用程序提供者提供當前可移植標準的對象。它們容許程序接受並進行鏈接,如發送和接受數據。爲了創建通訊通道,網絡通訊的每一個端點擁有一個套接字對象極爲重要。和大多數語言同樣,Python 支持面向鏈接和無鏈接,實現接口功能與步驟也大體相同。react

 

面向鏈接即須要先鏈接而後通信, 面向鏈接主要協議就是傳輸控制協議(tcp),要建立tcp套接字時須要指定套接字類型爲 SOCK_STRAM,表達了他做爲流套接字的特色。linux

無鏈接,顧名思義無需創建鏈接就能夠進行通信,這時數據到達順序、可靠性就沒法保證了。實現這種鏈接的協議就是用戶數據包協議(udp)。建立UDP時須要指定套接字類型爲 SOCK_DGRAM。web

服務器端一般是用來處理數據的,客戶端是用來請求數據的。服務端能夠同時處理多條數據。編程

同一個80端口windows

 

TCP服務器端:服務器

1. 第一步是建立socket對象。調用socket構造函數。如:網絡

socket = socket.socket( family, type )多線程

family參數表明地址家族,可爲AF_INET或AF_UNIX。AF_INET家族包括Internet地址,AF_UNIX家族用於同一臺機器上的進程間通訊。

type參數表明套接字類型,可爲SOCK_STREAM(流套接字)和SOCK_DGRAM(數據報套接字)。

2. 第二步是將socket綁定到指定地址。這是經過socket對象的bind方法來實現的:

socket.bind( address )

由AF_INET所建立的套接字,address地址必須是一個雙元素元組,格式是(host,port)。host表明主機,port表明端口號。若是端口號正在使用、主機名不正確或端口已被保留,bind方法將引起socket.error異常。

3. 第三步是使用socket套接字的listen方法接收鏈接請求。

socket.listen( backlog )

backlog指定最多容許多少個客戶鏈接到服務器。它的值至少爲1。收到鏈接請求後,這些請求須要排隊,若是隊列滿,就拒絕請求。

4. 第四步是服務器套接字經過socket的accept方法等待客戶請求一個鏈接。

connection, address = socket.accept()

調 用accept方法時,socket會時入「waiting」狀態。客戶請求鏈接時,方法創建鏈接並返回服務器。accept方法返回一個含有兩個元素的 元組(connection,address)。第一個元素connection是新的socket對象,服務器必須經過它與客戶通訊;第二個元素 address是客戶的Internet地址。

5. 第五步是處理階段,服務器和客戶端經過send和recv方法通訊(傳輸 數據)。服務器調用send,並採用字符串形式向客戶發送信息。send方法返回已發送的字符個數。服務器使用recv方法從客戶接收信息。調用recv 時,服務器必須指定一個整數,它對應於可經過本次方法調用來接收的最大數據量。recv方法在接收數據時會進入「blocked」狀態,最後返回一個字符 串,用它表示收到的數據。若是發送的數據量超過了recv所容許的,數據會被截短。多餘的數據將緩衝於接收端。之後調用recv時,多餘的數據會從緩衝區 刪除(以及自上次調用recv以來,客戶可能發送的其它任何數據)。

6. 傳輸結束,服務器調用socket的close方法關閉鏈接

僞代碼大體以下:

1 建立套接字,綁定套接字到當地地址,而後開始監聽鏈接。就是socket,bind,listen。

2 進入循環,不斷接受客戶端的鏈接請求,而後接收傳來的數據,固然也能夠發送給對方數據。就是accept一個鏈接,而後recv數據。

3 接收完畢能夠關閉套接字,close。

ss.socket(Socket.AF_INET,Socket.SOCK_STRAM)  #建立服務器套接字

ss.bind() #把本地地址綁到套接字上

ss.listen() #監聽鏈接

inf_loop: #服務器無限循環

cs=ss.accept() #接受客戶端的鏈接

comm._loop: #通訊循環

cs.recv()/cs.send() #對話

cs.close() #關閉客戶套接字

ss.close() #關閉服務器套接字

 

TCP客戶端:

1.  第一步是建立一個socket以鏈接服務器:socket = socket.socket( family, type )

2.  第二步是使用socket的connect方法鏈接服務器。對於AF_INET家族,鏈接格式以下:

socket.connect( (host,port) )

host表明服務器主機名或IP,port表明服務器進程所綁定的端口號。如鏈接成功,客戶就可經過套接字與服務器通訊,若是鏈接失敗,會引起socket.error異常。

3.  第三步是處理階段,客戶和服務器將經過send方法和recv方法通訊。

4.  傳輸結束,客戶經過調用socket的close方法關閉鏈接。

僞代碼以下:

1 建立套接字,而後鏈接遠端地址,socket ,connect。

2 創建鏈接以後開始發送數據。Send(data),固然能夠從緩衝區讀取服務器發來的數據。Recv(BUFF)

3 完畢後,關閉套接字。Close

cs=socket(Socket.AF_INET,Socket.SOCK_DGRAM)

#建立客戶套接字

cs.connect() #嘗試鏈接服務器

comm._loop: #通訊循環

cs.send()/cs.recv() #對話

cs.close() #關閉套接字

簡單的server 端和客戶端(tcp協議)

服務端

import socket   #socket模塊

import commands   #執行系統命令模塊

HOST='127.0.0.1'

PORT=8085

s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #定義socket類型,網絡通訊,TCP協議,前面是ipv4協議,後面的tcp協議。

s.bind((HOST,PORT))   #套接字綁定的IP與端口,host用0.0.0.1就會包含本機的全部ip

s.listen(1)         #開始TCP監聽,listen中的參數表示能夠多少客戶端來進行鏈接

while 1:

    conn,addr=s.accept()   #接受TCP鏈接,並返回新的套接字與IP地址

    print'Connected by',addr    #輸出客戶端的IP地址

    while 1:

        data=conn.recv(1024)    #把接收的數據實例化

        print data

        conn.sendall('got message from server:'+data.upper())

conn.close()     #關閉鏈接

 

客戶端:

import socket

HOST='127.0.0.1'

PORT=8085

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)      #定義socket類型,網絡通訊,TCP

s.connect((HOST,PORT))       #要鏈接的IP與端口

while 1:

    cmd=raw_input("Please input cmd:")       #與人交互,輸入命令

    s.sendall(cmd)      #把命令發送給對端

    data=s.recv(1024)     #把接收的數據定義爲變量

    print data         #輸出變量

s.close()   #關閉鏈接

 

 

 

變形:把上面的代碼改爲只能發3次

 

netstat -ano|findstr "1104"  查看windows上某個端口是否被佔用

netstat -anp|grep 8080   linux使用的

 

服務端:

import socket   #socket模塊

import commands   #執行系統命令模塊

HOST='127.0.0.1'

PORT=8085

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

#定義socket類型,網絡通訊,TCP

s.bind((HOST,PORT))   #套接字綁定的IP與端口

s.listen(5)         

#開始TCP監聽,listen中的參數表示能夠多少客戶端來進行鏈接

while 1:

    conn,addr=s.accept()   #接受TCP鏈接,並返回新的套接字與IP地址

    print'Connected by',addr    #輸出客戶端的IP地址

    while 1:

        try:

            data=conn.recv(1024)    #把接收的數據實例化

            print data

            conn.sendall('got message from server:'+data.upper())

        except Exception:

            conn.close()     #關閉鏈接

            break

客戶端:

import socket

HOST='127.0.0.1'

PORT=8085

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

#定義socket類型,網絡通訊,TCP

s.connect((HOST,PORT))       #要鏈接的IP與端口

times=3

while times>0:

    cmd=raw_input("Please input cmd:")       #與人交互,輸入命令

    s.sendall(cmd)      #把命令發送給對端

    data=s.recv(1024)     #把接收的數據定義爲變量

    print data         #輸出變量

    times-=1

s.close()   #關閉鏈接

 

 

 

稍微複雜一點的客戶端例子

服務端:

import time

if __name__ == '__main__':  

    import socket  

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

    sock.bind(('localhost', 8009))  

    sock.listen(5)  

    while True:  

        time.sleep(0.1)

        connection,address = sock.accept()

        while 1:  

            try:  

                connection.settimeout(5)  

                buf = connection.recv(1024) 

                print "got message from client:",buf 

                if buf == '1': 

                    print "1" 

                    connection.send('welcome to server!')  

                elif buf == '2':  

                    connection.send('please go out!')

                elif buf == "close":

                    connection.send('bye!') 

                    connection.close()

                    break

            except socket.timeout:  

                print 'time out'

                connection.close()

                break

            except Exception,e:

                print e 

                connection.close()

                break

 

客戶端:

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 8009))  

    import time  

    time.sleep(2)  

    sock.send('2')  

    print sock.recv(1023) 

    sock.send('1')

    print sock.recv(1024)  

    sock.send('close')

    print sock.recv(1024)  

    print "Done!" 

    sock.close()   

 

變形練習:服務端把客戶端傳來的消息所有存入文件中,而後給客戶端回傳一句話。

本身:

服務端:

import time
if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 10015))
    sock.listen(5)
    while True:
        time.sleep(0.1)
        connection,address = sock.accept()
        while 1:
            try:
                connection.settimeout(5)
                buf = connection.recv(1024)
                print "got message from client:",buf
                fp = open("e:\\test1.txt", "a")
                if buf == "close":
                    connection.send('bye!')
                    connection.close()
                    break
                else
:
                    fp.write(buf+"\n")
                    #fp.write("\n")
               
fp.close()
            except socket.timeout:
                print 'time out'
               
connection.close()
                break
            except
Exception,e:
                print e
                connection.close()
                break

客戶端:

if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('localhost', 10015))
    import time
    time.sleep(2)
    sock.send('2')
    sock.send('1')
    sock.send('close')
    print "Done!"
   
sock.close()

 

 

老師:

>>> import traceback
>>> try:
...     1/0
... except:
...     print "error"
...
error
>>> try:
...     1/0
... except Exception,e:
...     print e
...
integer division or modulo by zero
>>> try:
...     1/0
... except:
...     traceback.print_exc()
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

 

服務端:

# encoding=utf-8
import time
import random
import traceback
if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8009))
    sock.listen(5)
    AI_robot =[u'吃了麼?',u"最近漲工資了麼?",u"記得找對象",u"記得減肥"]
    while True:
        time.sleep(0.1)
        connection,address = sock.accept()
        while 1:
            try:
                connection.settimeout(5)
                buf = connection.recv(1024)
                fp=open("e:\\chat_record.txt","a")
                print "got message from client:",buf.decode("utf-8")
                if buf == "close":
                    connection.send('bye!')
                    connection.close()
                    break
                elif
buf=='q':
                    connection.close()
                    break
                else
:
                    fp.write(buf+"\n")
                    connection.send(str(time.ctime())+AI_robot[random.randint(0,3)].encode("utf-8"))
                fp.close()
            except socket.timeout:
                print 'time out'
               
traceback.print_exc()
                connection.close()
                break
            except
Exception,e:
                traceback.print_exc()
                print e
                connection.close()
                break

客戶端:

# encoding=utf-8
if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('localhost', 8009))
    import time
    while 1:
        try:
            data=raw_input("say to server:")
            sock.send(data.decode("gbk").encode("utf-8"))
            print sock.recv(1023).decode("utf-8")
            if data == "q":
                sock.close()
                break
        except
:
            print "exception"
   
print "Done!"

 

 

 

 

實現一個相似ssh的功能(僅支持Linux操做系統)

 

ip:39.106.41.11 
username:student
password:gloryroad987! 

 

 

服務器端:

import commands

if __name__ == '__main__':

    import socket

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

    sock.bind(('localhost', 8008))

    sock.listen(5)

    while True:

        connection,address = sock.accept()

        try:

            connection.settimeout(5)

            command = connection.recv(1024)

            print command

            code,result=commands.getstatusoutput(command)

            print code

            print result

        except socket.timeout:

            print 'time out'

        connection.close()

 

 

客戶端:

if __name__ == '__main__':

    import socket

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

    sock.connect(('localhost', 8008))

    import time

    time.sleep(2)

    sock.send('ls -al /home/wxh')

    print sock.recv(1024)

    sock.close() 

 

服務器端:
import commands
if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8001))
    sock.listen(5)
    while True:
        connection,address = sock.accept()
        try:
            connection.settimeout(5)
            command = connection.recv(1024)
            print command
            code,result=commands.getstatusoutput(command)
            print code
            print result
            connection.send(str(code))
            connection.send(result)
        except socket.timeout:
            print 'time out'
        connection.close()

客戶端:
if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('localhost', 8001))
    import time
    time.sleep(2)
    sock.send('ls -al /home/wxh')
    print "command status:",sock.recv(1)
    print "command result:",sock.recv(1024)
    sock.close()

 

使用Socket 傳送一個文件

服務器端

#-*- coding: UTF-8 -*-

import socket,time,SocketServer,struct,os,thread

host='127.0.0.1'

port=12307

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定義socket類型

s.bind((host,port)) #綁定須要監聽的Ip和端口號,tuple格式

s.listen(1)

 

 

def conn_thread(connection,address):  

    while True:

        try:

            connection.settimeout(600) #設置鏈接時間

            fileinfo_size=struct.calcsize('128sl')  #文件信息

            buf = connection.recv(fileinfo_size)  #接收此文件的信息在buf裏面

            if buf: #若是不加這個if,第一個文件傳輸完成後會自動走到下一句,須要拿到文件大小信息才能夠繼續執行

                filename,filesize =struct.unpack('128sl',buf)  #解包,解開後是兩個值

                filename_f = filename.strip('\00') #解出文件名,去掉C語言中的字符串\00結束

                filenewname = os.path.join('e:\\',('new_'+ filename_f))

                print 'file new name is %s, filesize is %s' %(filenewname,filesize)

                recvd_size = 0 #定義接收了的文件大小

                file = open(filenewname,'wb')

                print 'stat receiving...'

                while not recvd_size == filesize: #判斷收到的和原有文件是否是同樣大,不同繼續接收

                    if filesize - recvd_size > 1024:

                        rdata = connection.recv(1024)

                        recvd_size += len(rdata)

                    else:

                        rdata = connection.recv(filesize - recvd_size) #接收到的文件和告知的同樣大了,中止接收,斷定爲接收完畢

                        recvd_size = filesize

                    file.write(rdata)

                file.close()

                print 'receive done'

                #connection.close()

        except socket.timeout:

            connection.close()

 

 

while True:

    connection,address=s.accept()

    print('Connected by ',address)

    #thread = threading.Thread(target=conn_thread,args=(connection,address)) #使用threading也能夠

    #thread.start()

    thread.start_new_thread(conn_thread,(connection,address)) 

 

s.close()

 

客戶端

#-*- coding: UTF-8 -*-

import socket,os,struct

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

s.connect(('127.0.0.1',12307))

while True:

    

    filepath = raw_input('Please Enter chars:\r\n')

    if os.path.isfile(filepath):

        fileinfo_size=struct.calcsize('128sl') #定義打包規則

        #定義文件頭信息,包含文件名和文件大小

        fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)

        s.send(fhead) 

        print 'client filepath: ',filepath

        # with open(filepath,'rb') as fo: 這樣發送文件有問題,發送完成後還會發一些東西過去

        fo = open(filepath,'rb')

        while True:

            filedata = fo.read(1024)

            if not filedata:

                break

            s.send(filedata)

        fo.close()

        print 'send over...'

        #s.close()

SocketServer模塊的Fork方式(進程模式)linux 下執行

Server 端代碼

#!/usr/bin/python  

#encoding=utf-8  

  

from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler  

import time  

  

class Server(ForkingMixIn, TCPServer): #自定義Server類  ,以進程模式啓動

    pass  

  

class MyHandler(StreamRequestHandler):  

      

    def handle(self): #重載handle函數  

        addr = self.request.getpeername()  

        print 'Get connection from', addr #打印客戶端地址  

        data= self.rfile.readline().strip()  #客戶端發送的信息必須帶有回車,不然會一直等待客戶端繼續發送數據

        print data

        time.sleep(1) #休眠5秒鐘 

        if data: 

            self.wfile.write('This is a ForkingMixIn tcp socket server') #給客戶端發送信息  

  

host = '127.0.0.1'  

port = 8009

server = Server((host, port), MyHandler)  

  

server.serve_forever() #開始偵聽並處理鏈接

 

客戶端:

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 8009))  

    import time  

    time.sleep(2)  

    sock.send('ls -al /home/wxh'+"\n")  

    print sock.recv(1024)  

sock.close()  

 

註釋:

 多個鏈接同時到達服務器端的時候,每一個鏈接主進程都生成一個子進程專門用來處理此鏈接,而主進程則依舊保持在偵聽狀態。因主進程和子進程是同時進行的,因此不會阻塞新的鏈接。但因爲生成進程消耗的資源比較大,這種處理方式在有不少鏈接的時候會帶來性能問題。Server類須繼承ForkingMixIn和TCPServer兩個類。

 

SocketServer模塊的Fork方式(線程模式)linux 下執行

服務器端代碼:

#!/usr/bin/python  

#encoding=utf-8  

  

from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler  

import time  

  

class Server(ThreadingMixIn, TCPServer): #自定義Server類  ,以線程方式啓動

    pass  

  

class MyHandler(StreamRequestHandler):  

      

    def handle(self): #重載handle函數  

        addr = self.request.getpeername()  

        print 'Get connection from', addr #打印客戶端地址  

        data= self.rfile.readline().strip()  #客戶端發送的信息必須帶有回車,不然會一直等待客戶端繼續發送數據

        print data

        time.sleep(1) #休眠5秒鐘 

        if data: 

            self.wfile.write('This is a ForkingMixIn tcp socket server') #給客戶端發送信息  

  

host = '127.0.0.1'  

port = 8001  

server = Server((host, port), MyHandler)  

  

server.serve_forever() #開始偵聽並處理鏈接

 

 

 線程是一種輕量級的進程,比Fork消耗的資源更少,並且主線程和子線程之間具備相同的地址空間,處理效率高。但大量的使用線程會帶來線程之間的數據同步問題,處理很差可能使服務程序失去響應。上述與Fork方式中代碼基本相同,僅僅是採用的ThreadingMixIn類不一樣。

 

 

SocketServer模塊的Threading線程池方式

 

import SocketServer  

import threading

class MyTCPHandler(SocketServer.BaseRequestHandler):  

    def handle(self):  

        while True:  

          self.data = self.request.recv(1024).strip()  

          cur_thread = threading.current_thread()  

          print cur_thread  

          if not self.data:  

              print "client:%s leave!" % self.client_address[0]  

              break  

          print "%s wrote:%s" % (self.client_address[0], self.data)  

          self.request.sendall(self.data.upper())  

  

if __name__ == "__main__":  

    HOST, PORT = "127.0.0.1",8044  

    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)  

    server.serve_forever()    

if __name__ == "__main__":  

    HOST, PORT = "127.0.0.1",8001  

    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)  

server.serve_forever()  

 

#服務器能夠自動判斷客戶端是否還會發送數據

客戶端程序:

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 8044))  

    import time  

    time.sleep(2)  

    sock.send('ls -al /home/wxh'+"\n")  

    print sock.recv(1024)  

sock.close()  

 

使用socketserver 傳送一個文件

服務端:

#-*- coding: UTF-8 -*-

import socket,time,SocketServer,struct,os

host='127.0.0.1'

port=12307

ADDR=(host,port)

 

class MyRequestHandler(SocketServer.BaseRequestHandler):    

    def handle(self):      

        print('connected from:', self.client_address)

        while True:

            fileinfo_size=struct.calcsize('128sl') #定義文件信息。128s表示文件名爲128bytes長,l表示一個int或log文件類型,在此爲文件大小

            self.buf = self.request.recv(fileinfo_size)

            if self.buf: #若是不加這個if,第一個文件傳輸完成後會自動走到下一句

                self.filename,self.filesize =struct.unpack('128sl',self.buf) #根據128sl解包文件信息,與client端的打包規則相同

                print 'filesize is: ',self.filesize,'filename size is: ',len(self.filename) #文件名長度爲128,大於文件名實際長度

                self.filenewname = os.path.join('e:\\',('new_'+ self.filename).strip('\00')) #使用strip()刪除打包時附加的多餘空字符

                print self.filenewname,type(self.filenewname)

                recvd_size = 0 #定義接收了的文件大小

                file = open(self.filenewname,'wb')

                print 'stat receiving...'

                while not recvd_size == self.filesize:

                    if self.filesize - recvd_size > 1024:

                        rdata = self.request.recv(1024)

                        recvd_size += len(rdata)

                    else:

                        rdata = self.request.recv(self.filesize - recvd_size) 

                        recvd_size = self.filesize

                    file.write(rdata)

                file.close()

                print 'receive done'

        #self.request.close()

 

tcpServ = SocketServer.ThreadingTCPServer(ADDR, MyRequestHandler)  

print('waiting for connection...' )

tcpServ.serve_forever()

客戶端:

#-*- coding: UTF-8 -*-

import socket,os,struct

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

s.connect(('127.0.0.1',12307))

while True:

    

    filepath = raw_input('Please Enter chars:\r\n')

    if os.path.isfile(filepath):

        fileinfo_size=struct.calcsize('128sl') #定義打包規則

        #定義文件頭信息,包含文件名和文件大小

        fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)

        s.send(fhead) 

        print 'client filepath: ',filepath

        # with open(filepath,'rb') as fo: 這樣發送文件有問題,發送完成後還會發一些東西過去

        fo = open(filepath,'rb')

        while True:

            filedata = fo.read(1024)

            if not filedata:

                break

            s.send(filedata)

        fo.close()

        print 'send over...'

        #s.close()

Select :

I/O多路複用是在單線程模式下實現多線程的效果,實現一個多I/O併發的效果。

進程指定內核監聽哪些文件描述符(最多監聽1024fd)的哪些事件,當沒有文件描述符事件發生時,進程被阻塞;當一個或者多個文件描述符事件發生時,進程被喚醒。

當咱們調用select()時:

  1、上下文切換轉換爲內核態

  2、將fd從用戶空間複製到內核空間

  3、內核遍歷全部fd,查看其對應事件是否發生

  4、若是沒發生,將進程阻塞,當設備驅動產生中斷或者timeout時間後,將進程喚醒,再次進行遍歷

  5、返回遍歷後的fd

6、將fd從內核空間複製到用戶空間

 

 

一個任務:
1 www.sohu.com 

2 www.sogou.com 

3 www.gloryroad.cn 

4 www.baidu.com 


單進程或者單線程:
1->2->3-4

多進程或者多線程:
一個線程、一個進程:去執行一個訪問

select:
1 個線程:
1 www.sohu.com 

  5秒
2 www.sogou.com 

 4秒
3 www.gloryroad.cn 

 3秒
4 www.baidu.com 

 2秒  select不會等,一直髮請求,哪一個先返回用哪一個。

 

 

fd:file descriptor 文件描述符

 

fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout])

 

參數: 可接受四個參數(前三個必須)

rlist: wait until ready for reading

wlist: wait until ready for writing(通常不使用)

xlist: wait for an 「exceptional condition」

timeout: 超時時間

 

Server端:

import socket

import select

 

s = socket.socket()

s.bind(('127.0.0.1',8888))

s.listen(5)

r_list = [s,]

num = 0

while True:

 rl, wl, error = select.select(r_list,[],[],10)   #r_list讀的句柄,[][]後面兩個是異常的句柄,10是超時

執行過程:

 #第一次執行循環體:客戶端創建的鏈接的時候,rl和r_list分別是[s,]和[s,]

 #                  執行鏈接以後,r_list變爲了[s,conn]

 #第二次執行循環體:rl和r_list分別是[conn,]和[s,conn]

 

 

 num+=1

 print('counts is %s'%num)

 print("rl's length is %s"%len(rl))

 for fd in rl:

  if fd == s:

   conn, addr = fd.accept()

   r_list.append(conn)

   msg = conn.recv(200)

   conn.sendall(('first----%s'%conn.fileno()).encode())

  else:

   try:

    msg = fd.recv(200)

    fd.sendall('second'.encode())

   except ConnectionAbortedError:

    r_list.remove(fd)

 

 

s.close()

 

客戶端:

import socket

 

flag = 1

s = socket.socket()

s.connect(('127.0.0.1',8888))

while flag:

 input_msg = input('input>>>')

 if input_msg == '0':

  break

 s.sendall(input_msg.encode())

 msg = s.recv(1024)

 print(msg.decode())

 

s.close()

 

#注意客戶端輸入的數據須要外帶引號,例如:’hi’

Twisted 框架

 

 

服務器端:

# coding=utf-8

from twisted.internet.protocol import Protocol

from twisted.internet.protocol import Factory

from twisted.internet.endpoints import TCP4ServerEndpoint

from twisted.internet import reactor

 

 

clients = []

 

 

class Spreader(Protocol):

    def __init__(self, factory):

        self.factory = factory

 

    def connectionMade(self):

        self.factory.numProtocols = self.factory.numProtocols + 1

        self.transport.write(

            "歡迎來到Spread Site, 你是第%s個客戶端用戶!\n" % (self.factory.numProtocols)

        )

        print "new connect: %d" % (self.factory.numProtocols)

        clients.append(self)

 

    def connectionLost(self, reason):

        self.factory.numProtocols = self.factory.numProtocols - 1

        clients.remove(self)

        print "lost connect: %d" % (self.factory.numProtocols)

 

    def dataReceived(self, data):

        print "received data:",data

        self.transport.write(data)

 

 

class SpreadFactory(Factory):

    def __init__(self):

        self.numProtocols = 0

 

    def buildProtocol(self, addr):

        return Spreader(self)

 

 

endpoint = TCP4ServerEndpoint(reactor, 8007)

endpoint.listen(SpreadFactory())

reactor.run()

 

 

客戶端:

# coding=utf-8

from twisted.internet.protocol import Protocol, ClientFactory

from twisted.internet import reactor

import threading

import time

import sys

import datetime

 

 

class Echo(Protocol):

    def __init__(self):

        self.connected = False

 

    def connectionMade(self):

        self.connected = True

 

    def connectionLost(self, reason):

        self.connected = False

 

    def dataReceived(self, data):

        print "data from server:",data.decode("utf-8")

 

 

class EchoClientFactory(ClientFactory):

    def __init__(self):

        self.protocol = None

 

    def startedConnecting(self, connector):

        print "Start to Connect..."

 

    def buildProtocol(self, addr):

        print "Connected..."

        self.protocol = Echo()

        return self.protocol

 

    def clientConnectionLost(self, connector, reason):

        print "Lost connection. Reason: ", reason

 

    def clientConnectionFailed(self, connector, reason):

        print "Connection is failed, Reason: ", reason

 

 

bStop = False

 

 

def routine(factory):

    while not bStop:

        if factory.protocol and factory.protocol.connected:

            factory.protocol.transport.write("hello, I'm %s %s" % (

                sys.argv[0], datetime.datetime.now()        

            ))

 

            print sys.argv[0], datetime.datetime.now()

            

        time.sleep(5)

 

 

host = '127.0.0.1'

port = 8007

factory = EchoClientFactory()

reactor.connectTCP(host, port, factory)

threading.Thread(target=routine, args=(factory,)).start()

reactor.run()

bStop = True

 

——————————————————————————————————————

 

#!/usr/bin/python   

#encoding=utf-8   

   

from twisted.internet import reactor   

from twisted.internet.protocol import Protocol, Factory   

   

class EchoServer(Protocol):   

   

    def connectionMade(self):             #鏈接創建的時候   

        print 'Get connection from', self.transport.client   

        self.factory.numProtocols = self.factory.numProtocols+1   

        if self.factory.numProtocols > 2: #當鏈接超過2個的時候,斷開鏈接   

            self.transport.write("Too many connections, try later\n")   

            self.transport.loseConnection()  

            return   

        print 'Get connection from', self.transport.client  

        

   

    def connectionLost(self, reason):     #斷開鏈接   

        self.factory.numProtocols = self.factory.numProtocols-1   

   

    def dataReceived (self, data):        #將收到的數據返回給客戶端   

        self.transport.write(data)  

        print data 

        self.transport.loseConnection()     #收到數據後就斷開 

   

factory = Factory()  

factory.numProtocols = 0   

factory.protocol = EchoServer   

   

port = 1200   

reactor.listenTCP(port, factory)   

reactor.run() #進入循環

 

客戶端程序:

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 8001))  

    import time  

    time.sleep(2)  

    sock.send('ls -al /home/wxh'+"\n")  

    print sock.recv(1024)  

sock.close()  

 

 

twisted 官網的例子,保存爲echoServ.py:

#!/usr/bin/env python  

# coding: utf-8  

  

from twisted.internet.protocol import Protocol  

from twisted.internet.protocol import Factory  

from twisted.internet import reactor  

  

class Echo(Protocol):  

    '''協議類實現用戶的服務協議,例如 http,ftp,ssh 等'''  

    def __init__(self, factory):  

        self.factory = factory  

  

    def connectionMade(self):  

        '''鏈接創建時被回調的方法'''  

        self.factory.numProtocols = self.factory.numProtocols + 1  

        self.transport.write("Welcome! There are currently %d open connections.\n" %(self.factory.numProtocols,))  

  

    def connectionLost(self, reason):  

        '''鏈接關閉時被回調的方法'''  

        self.factory.numProtocols = self.factory.numProtocols - 1  

  

    def dataReceived(self, data):  

        '''接收數據的函數,當有數據到達時被回調'''

        print data  

        self.transport.write(data)  

  

  

class EchoFactory(Factory):  

    '''協議工廠類,當客戶端創建鏈接的時候,建立協議對象,協議對象與客戶端鏈接一一對應'''  

    numProtocols = 0  

    #protocol = Echo  

    def buildProtocol(self, addr):  

        return Echo(self)  

  

if __name__ == '__main__':  

      # 建立監聽端口  

    FACTORY = EchoFactory()  

    reactor.listenTCP(1200, FACTORY)  

      # 開始監聽事件  

reactor.run()

 

  協議工廠繼承自twisted.internet.protocol.Factory,需實現buildProtocol方法,協議工廠負責實例化協議類,不該該保存於鏈接相關的狀態信息,由於協議工廠類僅建立一個。協議類繼承自twisted.internet.protocol.Protocol,需實現dataReceived等方法,在協議類中實現應用協議,每個客戶端鏈接都會建立一個新的協議類對象。transport就是鏈接對象,經過它進行網絡寫數據。

 

客戶端:

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 1200))  

    import time  

    time.sleep(2)  

    sock.send('ls -al /home/wxh'+"\n")  

    print sock.recv(1024)  

    sock.send('ls -al /home/wxh'+"\n")  

    print sock.recv(1024) 

    sock.close()

使用守護進程來運行服務器端程序

 使用守護進程的方式運行服務,須要提供一個tac配置文件(這就是一個python文件,只是擴展名不一樣),而且在這個文件中須要建立一個應用程序對象,對象名必須是application。
       首先建立一個echo.tac文件:

#!/usr/bin/env python  

# coding: utf-8  

  

from twisted.application import service, internet  

import sys

sys.path.append("e:\\")   #echoServ.py保存在哪一個目錄,就把這個路徑設定到哪裏

from echoServ import EchoFactory  

  

# 建立應用程序對象  

application = service.Application('Echo 服務程序')  

  

# 建立 service 對象  

myServices = internet.TCPServer(1200, EchoFactory())  

  

# 設置 application 爲 service 的父元素  

myServices.setServiceParent(application)

 

 

而後用守護進程方式運行服務,運行命令:#twistd -y echo.tac。
      若想要中止服務:#kill -15 (pid)。

 

客戶端程序:

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 1200))  

    import time  

    time.sleep(2)  

    sock.send('ls -al /home/wxh'+"\n")  

    print sock.recv(1024)  

sock.close()

聊天的服務器版本:

服務器端:

from twisted.internet import protocol, reactor

 

class Echo(protocol.Protocol):

    def dataReceived(self, data):

        # As soon as any data is received, write it back

        self.transport.write(data)

        print data

 

class EchoFactory(protocol.Factory):

    def buildProtocol(self, addr):

        return Echo()

 

reactor.listenTCP(8000, EchoFactory())

reactor.run()

 

客戶端:

from twisted.internet import reactor, protocol

 

class EchoClient(protocol.Protocol):

    def connectionMade(self):

        self.transport.write("hello, world!")

 

    def dataReceived(self, data):

        print "Server said:", data

        self.transport.loseConnection()

 

    def connectionLost(self, reason):

        print "connection lost"

 

class EchoFactory(protocol.ClientFactory):

    def buildProtocol(self, addr):

        return EchoClient()

 

    def clientConnectionFailed(self, connector, reason):

        print "Connection failed - goodbye!"

        reactor.stop()

 

    def clientConnectionLost(self, connector, reason):

        print "Connection lost - goodbye!"

        reactor.stop()

 

reactor.connectTCP("localhost", 8000, EchoFactory())

reactor.run()

寫一個聊天的服務器:

服務器端:

#!/usr/bin/python  

#encoding=utf-8  

from twisted.internet.protocol import Factory  

from twisted.protocols.basic import LineReceiver  

from twisted.internet import reactor  

  

class Chat(LineReceiver):  

    def __init__(self, users):  

        self.users = users  

        self.name = None  

        self.state = "GETNAME"  

  

    def connectionMade(self):  

        self.sendLine("What's your name?\n")  

  

    def connectionLost(self, reason):  

        if self.users.has_key(self.name):  

            del self.users[self.name]  

  

    def lineReceived(self, line):  

        if self.state == "GETNAME":  

            self.handle_GETNAME(line)  

        else:  

            self.handle_CHAT(line)  

  

    def handle_GETNAME(self, name):  

        if self.users.has_key(name):  

            self.sendLine("Name taken, please choose another."+"\n")  

            return  

        self.sendLine("Welcome, %s!\n" % (name,))  

        self.name = name  

        self.users[name] = self  

        self.state = "CHAT"  

  

    def handle_CHAT(self, message):  

        message = "<%s> %s\n" % (self.name, message)  

        for name, protocol in self.users.iteritems():  

            if protocol == self:                

                self.sendLine(message+"\n") 

 

  

class ChatFactory(Factory):  

  

    def __init__(self):  

        self.users = {}    # maps user names to Chat instances  

  

    def buildProtocol(self, addr):  

        return Chat(self.users)  

  

if __name__ == '__main__':  

    reactor.listenTCP(1200, ChatFactory())  

    reactor.run()  

 

客戶端1:

#socket client end  

from socket import *  

  

s=socket(AF_INET,SOCK_STREAM)  

remote_host=gethostname()  

print 'remote_host:',remote_host  

port=1200  

s.connect((remote_host,port)) #發起鏈接  

print("Connected from",s.getsockname()) ##返回本地IP和端口  

print("Connected to",s.getpeername())  ##返回服務端IP和端口  

s.send('Jack\r\n')#發送一行字符串(以\r\n 結束)到服務器端   

s.send('hi\r\n')  

s.send('hello\r\n') 

  

print 'the msg i got from select server is:'  

print s.recv(1200)

客戶端2:

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 1200))  

    import time  

    time.sleep(2)

    sock.send('fosterwu\r\n')  

    print sock.recv(1020) 

    sock.send('hi\r\n')  

    print sock.recv(1020) 

    sock.send('good\r\n') 

    print sock.recv(120) 

    sock.send('gloryroad\r\n') 

    print sock.recv(120) 

    sock.close()

Twisted框架中對文件的操做
服務端:       

工廠有startFactory和stopFactory兩種方式來執行相關應用的建立與銷燬,下述代碼從客戶端接收到的信息都會被寫入文件中。
#!/usr/bin/env python  

# coding: utf-8  

  

from twisted.internet.protocol import Factory  

from twisted.protocols.basic import LineReceiver  

from twisted.internet import reactor  

  

class LoggingProtocol(LineReceiver):  

    def lineReceived(self, line):  

        self.factory.fp.write(line+'\n')  

        self.factory.fp.flush()

        self.sendLine("got it!") 

  

class LogfileFactory(Factory):  

    protocol = LoggingProtocol  

    def __init__(self, fileName):  

        self.file = fileName  

    def startFactory(self):  

        self.fp = open(self.file, 'a')  

    def stopFactory(self):  

        self.fp.close() 

        print "close" 

  

if __name__ == '__main__':  

      # 建立監聽端口  

    FACTORY = LogfileFactory("e:\\a.txt")  

    reactor.listenTCP(8007, FACTORY)  

      # 開始監聽事件  

reactor.run()

 

客戶端:

 

if __name__ == '__main__':  

    import socket  

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

    sock.connect(('localhost', 8007))  

    import time  

    time.sleep(2)

    sock.send('fosterwu\r\n')  

    print sock.recv(1020) 

    sock.send('hi\r\n')  

    print sock.recv(1020) 

    sock.send('good\r\n') 

    print sock.recv(120) 

    sock.send('gloryroad\r\n') 

    print sock.recv(120) 

    sock.close()

 

 

Udp Server 端:

from socket import *  

from time import ctime  

  

HOST = ''  

PORT = 1200

BUFSIZ = 128  

ADDR = (HOST, PORT)  

  

#建立一個服務器端UDP套接字  

udpServer = socket(AF_INET, SOCK_DGRAM)  

#綁定服務器套接字  

udpServer.bind(ADDR)  

  

while True:  

    print 'waiting for message...'  

#接收來自客戶端的數據  

    data, addr = udpServer.recvfrom(BUFSIZ) 

    print "got message:",data 

#向客戶端發送數據  

    udpServer.sendto('[%s] %s' % (ctime(), data), addr)  

    print '...received from and returned to:', addr  

      

udpServer.close()

 

Udp 的客戶端:

 

#encoding=utf-8

from socket import *

 

HOST = 'localhost'

PORT = 1200

BUFSIZ = 128

ADDR = (HOST, PORT)

 

# 建立客戶端UDP套接字

udpClient = socket(AF_INET, SOCK_DGRAM)

 

while True:

  data = raw_input('>')

  if not data:

    break

  # 向服務器端發送數據

  udpClient.sendto(data, ADDR)

  # 接收來自服務器端的數據

  data, ADDR = udpClient.recvfrom(BUFSIZ)

  print data

  if not data:

    break

 

udpClient.close()

 

 

參考文獻:

http://blog.csdn.net/taiyang1987912/article/details/40376067

http://www.javashuo.com/article/p-fjrqjhce-d.html

 

 

基於廣播的聊天程序

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

SO_LINGER選項用來設置當調用closesocket時是否立刻關閉socket; 

SO_REUSEADDR用於對TCP套接字處於TIME_WAIT狀態下的socket,才能夠重複綁定使用。server程序老是應該在調用bind()以前設置SO_REUSEADDR套接字選項。TCP,先調用close()的一方會進入TIME_WAIT狀態

 

服務器端:Linux能夠執行

import socket, select

 

#Function to broadcast chat messages to all connected clients

def broadcast_data (sock, message):

    #Do not send the message to master socket and the client who has send us the message

    for socket in CONNECTION_LIST:

        if socket != server_socket and socket != sock :

            try :

                socket.send(message)

            except :

                # broken socket connection may be, chat client pressed ctrl+c for example

                socket.close()

                CONNECTION_LIST.remove(socket)

 

if __name__ == "__main__":

     

    # List to keep track of socket descriptors

    CONNECTION_LIST = []

    RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2

    PORT = 6001

     

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

    # this has no effect, why ?

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

    server_socket.bind(("0.0.0.0", PORT))

    server_socket.listen(10)

 

    # Add server socket to the list of readable connections

    CONNECTION_LIST.append(server_socket)

 

    print "Chat server started on port " + str(PORT)

 

    while 1:

        # Get the list sockets which are ready to be read through select

        read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])

 

        for sock in read_sockets:

            #New connection

            if sock == server_socket:

                # Handle the case in which there is a new connection recieved through server_socket

                sockfd, addr = server_socket.accept()

                CONNECTION_LIST.append(sockfd)

                print "Client (%s, %s) connected" % addr

                 

                broadcast_data(sockfd, "[%s:%s] entered room\n" % addr)

             

            #Some incoming message from a client

            else:

                # Data recieved from client, process it

                try:

                    #In Windows, sometimes when a TCP program closes abruptly,

                    # a "Connection reset by peer" exception will be thrown

                    data = sock.recv(RECV_BUFFER)

                    if data:

                        broadcast_data(sock, "\r" + '<' + str(sock.getpeername()) + '> ' + data)                

                 

                except:

                    broadcast_data(sock, "Client (%s, %s) is offline" % addr)

                    print "Client (%s, %s) is offline" % addr

                    sock.close()

                    CONNECTION_LIST.remove(sock)

                    continue

     

server_socket.close()

 

客戶端:

import socket, select, string, sys

 

def prompt() :

    

    sys.stdout.write('<You> ')

    sys.stdout.flush()

 

#main function

if __name__ == "__main__":

     

    if(len(sys.argv) < 3) :

        print 'Usage : python telnet.py hostname port'

        sys.exit()

     

    host = sys.argv[1]

    port = int(sys.argv[2])

     

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

    s.settimeout(6)

     

    # connect to remote host

    try :

        s.connect((host, port))

    except :

        print 'Unable to connect'

        sys.exit()

     

    print 'Connected to remote host. Start sending messages'

    prompt()

     

    while 1:

        rlist = [sys.stdin, s]

        #s.connect((host, port)) 

        # Get the list sockets which are readable

        read_list, write_list, error_list = select.select(rlist , [], [])

         

        for sock in read_list:

            #incoming message from remote server

            if sock == s:

                data = sock.recv(4096)

                

                if not data :

                    print '\nDisconnected from chat server'

                    sys.exit()

                else :

                    #print data

                    sys.stdout.write(data)

                    prompt()

             

            #user entered a message

            else :

                

                msg = sys.stdin.readline()

                s.send(msg)

                prompt()

 

執行:

須要多開幾個客戶端,才能看到廣告效果,執行以下:

python client.py 127.0.0.1 6001

 

Unix下是sock,windows下是winsock,中間語法有不少是不一樣的,因此windows運行client程序有問題

相關文章
相關標籤/搜索