Python之Socket&異常處理

Socket服務器

Socket用於描述IP地址和端口號,每一個應用程序都是經過它來進行網絡請求或者網絡應答。網絡

socket模塊和file模塊有類似之處,file主要對某個文件進行打開、讀寫、關閉操做。socket主要對服務端和客戶端應用程序進行打開、讀寫、關閉。ssh

經常使用方法:socket

sk.bind(address)ide

  s.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。spa

sk.listen(backlog)code

   開始監聽傳入鏈接。backlog指定在拒絕鏈接以前,能夠掛起的最大鏈接數量。backlog等於5,表示內核已經接到了鏈接請求,但服務器尚未調用accept進行處理的鏈接個數最大爲5。這個值不能無限大,由於要在內核中維護鏈接隊列。server

sk.setblocking(bool)對象

  是否阻塞(默認True),若是設置False,那麼accept和recv時一旦無數據,則報錯。blog

sk.accept()

 接受鏈接並返回(conn,address),其中conn是新的套接字對象,能夠用來接收和發送數據。address是鏈接客戶端的地址。

 接收TCP 客戶的鏈接(阻塞式)等待鏈接的到來。

sk.connect(address)

 鏈接到address處的套接字。通常,address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。

sk.recv(bufsize[,flag])

 接受套接字的數據。數據以字符串形式返回,bufsize指定最多能夠接收的數量。flag提供有關消息的其餘信息,一般能夠忽略。

sk.send(string[,flag])

 將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容所有發送。

sk.sendall(string[,flag])

 將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。

    內部經過遞歸調用send,將全部內容發送出去。

sk.close()

  關閉套接字。

客戶端與服務端socket通訊示例:

服務器端:

import socket
ip_port = ('127.0.0.1',8888)

sk = socket.socket()
sk.bind(ip_port)#向系統申請地址和端口號,並綁定
sk.listen(5)

while True:
    print 'wating...'
    conn,addr = sk.accept()#接收客戶端的地址和端口,創建鏈接
    client_data = conn.recv(1024)#接收客戶端數據
    print client_data
    conn.sendall('recive your message!')
    conn.close()
socket_server

客戶端:

import socket

ip_port = ('127.0.0.1',8888)
sk = socket.socket()#生成socket句柄實例
sk.connect(ip_port)#鏈接
sk.sendall('hello hello hello...')#向服務器端發送
server_reply = sk.recv(1024)#接收服務器迴應
print server_reply
sk.close()
socket_client

連續交互示例:

服務端:

import socket
ip_port = ('127.0.0.1',8888)

sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
    print 'ready...'
    conn,addr = sk.accept()
    while True:
        client_data = conn.recv(1024)
        print client_data
        response = raw_input('>>')
        conn.sendall(response)
    conn.close()
socket_server

客戶端:

import socket

ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.connect(ip_port)
while True:
    request = raw_input('>>')
    sk.sendall(request)
    server_reply = sk.recv(1024)
    print server_reply
sk.close()
socket_client

 

socket實現ssh功能:

總體思路:

#服務端監聽端口ip及端口,客戶端發起鏈接請求,服務器端確認鏈接,並開始準備接收客戶端發來的消息(循環,若是沒有收到客戶端發來的消息,退出循環)。
#收到客戶端發來的命令,執行該命令,並將該命令執行結果的大小返回給客戶端(ack_msg)。
#客戶端收到ack_msg後,會給服務端發送確認接收命名執行結果的client_ack_msg,而後服務端開始發送執行結果(此過程是爲了不socket粘包問題)客戶端開始接收,並根據ack_msg中標記的大小來循環接收命令執行結果。

 服務端:

import socket
import os

ip_port = ('127.0.0.1',8888)

sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)

while True:
    print 'Socker Server is ready...'
    conn,addr = sk.accept()
    while True:
        client_data = conn.recv(1024)
        if not client_data:#沒有接受的到客戶端消息,退出循環
            break
        print client_data
        response = os.popen(client_data).read()#獲取命令執行結果
        if len(response) == 0:#命令執行結果爲空,告知客戶端,不然客戶端會一直等待接受響應
            conn.sendall('command no result!')
        else:
            ack_msg = 'cmd result size is | %s' % len(response)#將命令執行結果的長度發給客戶端,‘|’符號便於客戶端切割處理
            conn.sendall(ack_msg)#給客戶端發送ack
            client_ack = conn.recv(1024)#接收客戶端發來的確認接收數據ack
            print client_ack
            if client_ack == 'client_ready_to_recv':#確認接收到客戶端發來確認接收數據的ack
                conn.sendall(response)#開始發送命令執行結果
    conn.close()
SSH_Server

客戶端:

import socket

ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.connect(ip_port)
while True:
    cmd = raw_input('cmd:')
    if len(cmd) == 0:#輸入爲空時,繼續要求輸入
        continue
    if cmd == 'q':#輸入爲q時退出
        break
    sk.sendall(cmd)
    server_ack_msg = sk.recv(1024)
    cmd_res_size = int(server_ack_msg.split('|')[1])#命令執行結果的大小
    print server_ack_msg
    if server_ack_msg.split('|')[0] =='cmd result size is':#若是收到的是服務端發來的ack信息
        print 'hello'
        sk.sendall('client_ready_to_recv')#給服務端發送確認接收數據的ack
    res = ''
    recv_size = 0
    while recv_size < cmd_res_size:#只要接收的比總大小小,就繼續接收
        data = sk.recv(1024)
        recv_size += len(data)#將本次接收到的大小加到已接收裏
        res += data#拼接接收的內容
    else:
        print res
sk.close()
SSH_Client

以上示例都是能一對一的鏈接,下面是一個服務端接受多個客戶端鏈接的實例:

服務端:

import  SocketServer

class Handler(SocketServer.BaseRequestHandler):
    def handle(self):
        print 'new conn: %s' % str(self.client_address)
        while True:
            data = self.request.recv(1024)
            if not data:
                break
            else:
                print 'client said:'+data
                self.request.sendall(data)

if __name__ == '__main__':
    host,port = 'localhost',8888
    server = SocketServer.ThreadingTCPServer((host,port),Handler)
    server.serve_forever()
Socket_server

客戶端:

import  socket

host_port ='localhost',8888
sk = socket.socket()
sk.connect(host_port)
while True:
    msg = raw_input('>>:').strip()
    sk.sendall(msg)
    server_reply = sk.recv(1024)
    print 'server reply:'+server_reply
sk.close()
Socket_client

 

異常處理

程序運行出現異常時,避免將該異常展示給用戶。根據異常處理機制,能夠自定義異常拋出信息。

沒有添加異常處理:

a = range(10)
print a[11]

運行結果拋出異常:IndexError: list index out of range,程序中止運行

有異常處理:

a = range(10)

try:
    print a[11]
except Exception:
    print '超出範圍' #自定義異常提示
     

try:
    print a[11]
except Exception as e:
    print '超出範圍' #自定義異常提示
    print e #系統拋出的異常

運行結果:程序正常退出

 

經常使用的異常:

AttributeError 試圖訪問一個對象沒有的樹形,好比foo.x,可是foo沒有屬性x
IOError 輸入/輸出異常;基本上是沒法打開文件
ImportError 沒法引入模塊或包;基本上是路徑問題或名稱錯誤
IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
IndexError 下標索引超出序列邊界,好比當x只有三個元素,卻試圖訪問x[5]
KeyError 試圖訪問字典裏不存在的鍵
KeyboardInterrupt Ctrl+C被按下
NameError 使用一個還未被賦予對象的變量
SyntaxError Python代碼非法,代碼不能編譯(我的認爲這是語法錯誤,寫錯了)
TypeError 傳入對象類型與要求的不符合
UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是因爲另有一個同名的全局變量,致使你覺得正在訪問它
ValueError 傳入一個調用者不指望的值,即便值的類型是正確的

捕獲ctrl-c:

while True:
    try:
        input = raw_input('input:')
    except KeyboardInterrupt:
        print '請不要按ctrl+c'#輸入ctrl+c後,程序依然運行。

自定義異常:

pass:關於類方法的補充

class a:
    def __init__(self,name):#當建立該類的一個實例時,該方法馬上執行。
        self.name = name
    def __str__(self):#返回字符串給用戶
        return 'hello %s' % self.name

if __name__ == '__main__':
    p = a('ahaii')
    print p

#運行結果:hello ahaii
class a:
    def __init__(self,name):
        self.name = name

if __name__ == '__main__':
    p = a('ahaii')
    print p

#運行結果:< at 0x7f2b89ecff38>,只返回內存地址

自定義一個異常:

class ahaiiException(Exception):
    def __init__(self,msg):
        self.msg = msg
    def __str__(self):
        return self.msg

try:
    raise ahaiiException('自定義異常')#主動觸發異常
except ahaiiException as e:
    print e

'自定義異常'

另外的格式:

try:
    command
except Exception:
    command
finally:
    command#不管是否異常,finally都會執行。

斷言:assert

a = 1
assert a == 1 

判斷a == 1是否成立,若不成立,程序停止。

相關文章
相關標籤/搜索