利用socket,咱們就能夠寫網絡編程的服務端和客戶端實現通訊了,附上兩端的基本實現方法圖:shell
從圖上能夠看到,網絡編程須要服務端和客戶端,應該先起服務端,再起客戶端。編程
服務端的大體步驟爲:服務器
客戶端的大體步驟爲:網絡
這裏附上關於socket最簡單的代碼:多線程
服務端:併發
import socket ###導入socket模塊 s = socket.socket() ###實例化一個socket對象 server_addr = ("127.0.0.1",12345) s.bind(server_addr) ###綁定IP和端口 s.listen(1) ###在拒絕鏈接前,最大接收掛起的客戶端爲1個 conn,addr = s.accept() ###等待接收數據 while True: try: recv_data = conn.recv(1024) ###接收數據,最多收1024個字節,接收的類型必須是bytes類型 send_data = str(recv_data,encoding="utf-8").upper() ###把接收的數據轉變成大寫字母 conn.send(bytes(send_data,encoding="utf-8")) ###發送數據,類型必須是bytes類型 except Exception: continue conn.close() ###關閉鏈接
客戶端:ssh
import socket ###導入socket模塊 s = socket.socket() ###實例化socket對象 server_info = ("127.0.0.1",12345) s.connect(server_info) ###鏈接服務端的地址和端口 while True: send_data = input(">> ").strip() ###客戶端輸入數據 if send_data == "exit":break if len(send_data) == 0:continue s.send(bytes(send_data,encoding="utf8")) ###客戶端發送數據,類型必須是bytes類型 recv_data = s.recv(1024) ###客戶端接收數據 print(str(recv_data,encoding="utf8")) s.close() ###關閉鏈接
先執行服務端代碼,後執行客戶端代碼,在客戶端執行的結果以下:socket
>> ls LS >> fuck FUCK >> haha HAHA >> exit Process finished with exit code 0
代碼解析:以上代碼實現了最簡單的socket通訊,這裏須要注意的是,accept()、recv()、send()方法都是阻塞類型,參數不能爲空,假如參數爲空,程序就會阻塞住,不能執行下面的代碼。函數
利用socket,咱們能夠實如今客戶端相似ssh鏈接服務端同樣,執行系統命令,下面是具體代碼及講解。編碼
服務端:
import socket ###導入socket模塊 import subprocess ###導入系統命令模塊 s = socket.socket() ###實例化socket對象 server_addr = ("127.0.0.1",12345) s.bind(server_addr) ###綁定地址和端口 s.listen(1) ###在拒絕鏈接前,最大接收掛起的客戶端爲1個 while True: conn,addr = s.accept() ###等待接收數據 while True: try: recv_data = conn.recv(1024) ###接收數據 print(recv_data) result = subprocess.Popen(str(recv_data,encoding="utf8"),shell=True,stdout=subprocess.PIPE) ###執行系統命令 result_base = result res = result_base.stdout.read() ###獲取命令結果 result_base.communicate() ###獲得命令返回值 if result_base.returncode: ###判斷命令返回值 send_data = "cmd error" elif len(res) == 0: send_data="None" else: send_data=str(res,encoding="utf-8") send_data = bytes(send_data,encoding="utf8") len_send_data = bytes(len(send_data)) ###發送字節類型數據 conn.send(send_data) ###發送字節類型數據 except Exception: break conn.close()
客戶端和上個例子同樣代碼便可:
import socket ###導入socket模塊 s = socket.socket() ###實例化socket對象 server_info = ("127.0.0.1",12345) s.connect(server_info) ###鏈接服務端的地址和端口 while True: send_data = input(">> ").strip() ###客戶端輸入數據 if send_data == "exit":break if len(send_data) == 0:continue s.send(bytes(send_data,encoding="utf8")) ###客戶端發送數據,類型必須是bytes類型 recv_data = s.recv(1024) ###客戶端接收數據 print(str(recv_data,encoding="utf8")) s.close() ###關閉鏈接
先執行服務端,再執行客戶端,客戶端執行結果以下:
>> ls socket_client.py socket_client1.py socket_client2.py socket_server.py socketclient_test.py socketserver_test.py test.py >> df -h Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk1 233Gi 173Gi 59Gi 75% 45438905 15542341 75% / devfs 180Ki 180Ki 0Bi 100% 622 0 100% /dev map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /home >> abcd cmd error
附上socket模塊的其餘方法:
socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)
參數一:地址簇
socket.AF_INET IPv4(默認)
socket.AF_INET6 IPv6
socket.AF_UNIX 只可以用於單一的Unix系統進程間通訊
參數二:類型
socket.SOCK_STREAM 流式socket , for TCP (默認)
socket.SOCK_DGRAM 數據報式socket , for UDP
socket.SOCK_RAW 原始套接字,普通的套接字沒法處理ICMP、IGMP等網絡報文,而SOCK_RAW能夠;其次,SOCK_RAW也能夠處理特殊的IPv4報文;此外,利用原始套接字,能夠經過IP_HDRINCL套接字選項由用戶構造IP頭。
socket.SOCK_RDM 是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在須要執行某些特殊操做時使用,如發送ICMP報文。SOCK_RAM一般僅限於高級用戶或管理員運行的程序使用。
socket.SOCK_SEQPACKET 可靠的連續數據包服務
參數三:協議
0 (默認)與特定的地址家族相關的協議,若是是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議
sk.bind(address)
s.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。
sk.listen(backlog)
開始監聽傳入鏈接。backlog指定在拒絕鏈接以前,能夠掛起的最大鏈接數量。
backlog等於5,表示內核已經接到了鏈接請求,但服務器尚未調用accept進行處理的鏈接個數最大爲5
這個值不能無限大,由於要在內核中維護鏈接隊列
sk.setblocking(bool)
是否阻塞(默認True),若是設置False,那麼accept和recv時一旦無數據,則報錯。
sk.accept()
接受鏈接並返回(conn,address),其中conn是新的套接字對象,能夠用來接收和發送數據。address是鏈接客戶端的地址。
接收TCP 客戶的鏈接(阻塞式)等待鏈接的到來
sk.connect(address)
鏈接到address處的套接字。通常,address的格式爲元組(hostname,port),若是鏈接出錯,返回socket.error錯誤。
sk.connect_ex(address)
同上,只不過會有返回值,鏈接成功時返回 0 ,鏈接失敗時候返回編碼,例如:10061
sk.close()
關閉套接字
sk.recv(bufsize[,flag])
接受套接字的數據。數據以字符串形式返回,bufsize指定最多能夠接收的數量。flag提供有關消息的其餘信息,一般能夠忽略。
sk.recvfrom(bufsize[.flag])
與recv()相似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
sk.send(string[,flag])
將string中的數據發送到鏈接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。即:可能未將指定內容所有發送。
sk.sendall(string[,flag])
將string中的數據發送到鏈接的套接字,但在返回以前會嘗試發送全部數據。成功返回None,失敗則拋出異常。
內部經過遞歸調用send,將全部內容發送出去。
sk.sendto(string[,flag],address)
將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議。
sk.settimeout(timeout)
設置套接字操做的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。通常,超時期應該在剛建立套接字時設置,由於它們可能用於鏈接的操做(如 client 鏈接最多等待5s )
sk.getpeername()
返回鏈接套接字的遠程地址。返回值一般是元組(ipaddr,port)。
sk.getsockname()
返回套接字本身的地址。一般是一個元組(ipaddr,port)
sk.fileno()
SocketServer內部使用 IO多路複用 以及 「多線程」 和 「多進程」 ,從而實現併發處理多個客戶端請求的Socket服務端。即:每一個客戶端請求鏈接到服務器時,Socket服務端都會在服務器是建立一個「線程」或者「進程」 專門負責處理當前客戶端的全部請求。
附上服務端代碼:
import socketserver ###導入socketserver模塊 import subprocess ###導入系統命令模塊 class MySocketServer(socketserver.BaseRequestHandler): ###建立一個類繼承socketserver.BaseRequestHandler def handle(self): ###建立handle方法,必須叫handle,由於其父類有handle方法 self.request.sendall(bytes("歡迎致電10086...",encoding="utf8")) ###向全部客戶端發送一條語句 while True: data = self.request.recv(1024) ###接收數據 str_data = data.decode() ###轉碼爲字符串 ret = subprocess.Popen(str_data,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) ###執行系統命令 result = ret.stdout.read() if not result: result = ret.stderr.read() if len(result) == 0: result = bytes("執行結果爲空",encoding="utf-8") if len(str_data) == 0: break print("[%s] says: [%s]" % (self.client_address,str_data)) ###打印客戶端地址以及數據 self.request.sendall(result) ###發送數據 if __name__ == '__main__': server = socketserver.ThreadingTCPServer(("127.0.0.1",8007),MySocketServer) ###實例化socketserver.ThreadingTCPServer的對象,參數一是地址和端口,參數二是建立的類 server.serve_forever() ###執行對象的serve_forever方法,做用是循環監聽
客戶端:
import socket ip_port = ('127.0.0.1',8007) s = socket.socket() s.connect(ip_port) welcome_msg = s.recv(1024) print("from server: ",welcome_msg.decode()) while True: send_data = input(">> ").strip() if len(send_data) == 0:continue s.send(bytes(send_data,encoding='utf8')) recv_data = s.recv(1024) print(str(recv_data,encoding="utf8")) s.close()
客戶端代碼依舊是socket模塊中的例子,先執行服務端代碼,當兩次及以上執行客戶端代碼時,發現輸入的命令都能獲得返回值,實現了併發的效果。