Python的socket模塊與交互式指令

socket簡介

在編程的過程當中,咱們須要使用網絡編程,這時咱們不得不和網絡通訊的底層基礎打交道了.咱們必須讓本身傳輸的數據符合網絡通訊的基本協議,即TCP/IP協議,可是網絡通訊協議自己很複雜.咱們不可能在編程的過程當中還本身去對數據進行封包處理,這是便出現了socket幫助咱們處理相關的數據傳輸,在其餘語言裏也能夠使用socket幫咱們處理相關問題.python

socket本質就是一組接口,是在應用層與TCP/IP協議族通訊中間的一個抽象層,龐大複雜的TCP/IP協議族咱們即可以不用過多關注,而只須要經過socket提供的接口就能夠相互鏈接.shell

socke模塊中的基本命令

s.bind()    在服務端綁定(主機,端口號)到套接字
s.listen()  開始TCP監聽
s.accept()  被動接受TCP客戶的鏈接,(阻塞式)等待鏈接的到來,收到的是元組形式,包含內容和地址
s.connect()     主動初始化TCP服務器鏈接
s.connect_ex()  connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
s.recv()            接收TCP數據
s.send()            發送TCP數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完)
s.sendall()         發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完)
s.recvfrom()        接收UDP數據
s.sendto()          發送UDP數據
s.getpeername()     鏈接到當前套接字的遠端的地址
s.getsockname()     當前套接字的地址
s.getsockopt()      返回指定套接字的參數
s.setsockopt()      設置指定套接字的參數
s.close()           關閉套接字
s.setblocking()     設置套接字的阻塞與非阻塞模式
s.settimeout()      設置阻塞套接字操做的超時時間
s.gettimeout()      獲得阻塞套接字操做的超時時間
s.fileno()          套接字的文件描述符
s.makefile()        建立一個與該套接字相關的文件

TCP的服務端和客戶端示例

TCP服務端

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
##網絡編程須要調用AF_INET,而SOCK_STREAM表明數據流,即TCP方式
phone.bind(('192.168.1.101',9000))
##綁定IP地址和端口,必須是元組
phone.listen(5)
##設置監聽數量
while True:
    conn, addr = phone.accept()
    ##獲得內容對象和地址
    while True:
        try:  ##異常處理特殊狀況
            feedback = conn.recv(1024)
            ##獲得內容對象傳來的值,裏面規定字節數量
            print(feedback.decode("UTF-8"))
            ##編譯,由於只有被編譯的字符才能被傳輸
            msg = input(">>:")
            conn.send(msg.encode("UTF-8"))
            ##發送
        except Exception:
            break

conn.close() ##關閉鏈接了的對象
phone.close() ##關閉服務器

TCP客戶端

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect_ex(('192.168.1.101',9000)) ##鏈接對象
while True:
    msg = input(">>:")
    phone.send(msg.encode("UTF-8")) ##發送消息
    feedback = phone.recv(1024) ##返回消息
    print(feedback.decode("UTF-8"))
phone.close() ##關閉對象

問題

可能在屢次開啓關閉服務器的時候,會出現IP地址已經被使用了的狀況,咱們能夠用如下方法解決:編程

phone = socket(AF_INIT, SOCKET_STREAM)
phone.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
phone.bind(('192.168.1.101',9000))

####UDP的服務端和客戶端示例緩存

UDP的服務端

from socket import *
ip_addr = ('192.168.1.101',9000)
buff_size = 1024
udp_server = socket(AF_INET, SOCK_DGRAM)
##SOCK_DGRAM表示UDP方式
udp_server.bind(ip_addr)
##綁定IP地址和端口
while True:
    data, addr = udp_server.recvfrom(buff_size)
    ##受信息
    print(data.decode("utf-8"))
    udp_server.sendto(data.upper(), addr)
    ##發信息

UDP的客戶端

from socket import *

ip_addr = ('192.168.1.101',9000)
buff_size = 1024
udp_client = socket(AF_INET, SOCK_DGRAM)
while True:
    msg = input(">>:")
    udp_client.sendto(msg.encode("utf-8"), ip_addr)
    ##不須要綁定,只須要傳一個地址
    date, addr = udp_client.recvfrom(buff_size)
    print(date.decode("utf-8"))

subprogress模塊

 
 

TCP和UDP比較

TCP UDP
傳輸方式 數據流(STREAM) 數據報(DGRAM)
致使問題 粘包 丟包
接待對象 一個 多個
鏈接 須要鏈接線 不須要鏈接線

TCP是以數據流的方式,在數據不少時,會屢次地不份內容關聯性地傳輸給對方內核態緩存,當用戶態緩存來取數據時,並不知道數據的開頭和結尾,那麼就出現了粘包現象.所以粘包問題主要仍是由於接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的,因此經過TCP方式不能發送空.服務器

然而,UDP方式是以信息爲單位傳輸數據,在數據裏面包含了開頭和結尾的界限,所以不會出現粘包的現象,可是當傳輸來的數據被儲存在內核態緩存裏面時,若是應用態緩存沒有一次性的取完數據,那麼數據就會被丟棄網絡

解決了粘包現象的遠程指令程序

客戶端

from socket import *

ip_port = ('127.0.0.1', 9000)

client = socket(AF_INET, SOCK_STREAM)
client.connect_ex(ip_port)
while True:
    command = input(">>:")
    if not command: continue
    client.send(command.encode("utf-8"))
    feedback_of_length = int(client.recv(1024).decode("gbk"))  #先獲得數據的長度
    client.send("OK".encode("gbk"))   #而且發送能夠傳輸了的命令
    all_data = b""  
    data_length = 0
    while data_length < feedback_of_length:
        all_data += client.recv(1024)  #循環地把數據聯結在一塊兒
        data_length = len(all_data)   
    last_date = all_data.decode("gbk")  ##最後解碼
    print(last_date)
    
client.close()

服務端

from socket import *
import subprocess

ip_port = ('127.0.0.1', 9000)
buff_size = 5

server = socket(AF_INET, SOCK_STREAM)
server.bind(ip_port)
server.listen(buff_size)
while True:
    conn, addr = server.accept()
    while True:
        try:
            cmd = conn.recv(1024)
            if not cmd: continue
            res = subprocess.Popen(cmd.decode("utf-8"), shell=True,
                                   stderr = subprocess.PIPE,
                                   stdin = subprocess.PIPE,
                                   stdout = subprocess.PIPE
            )
            err = res.stderr.read()
            if not res:
                get_res = "Run Perfectly"
            elif err:
                get_res = err
            else:
                get_res = res.stdout.read()
            length_of_res = len(get_res)  #發送數據長度
            conn.send(str(length_of_res).encode("gbk")) 
            last_info = conn.recv(1024)   #獲得是否傳輸的指令
            if last_info.decode("utf-8") == "OK":
                 conn.send(get_res)
        except Exception:
            break

socketserver實現併發

TCP版服務端

import socketserver

class MyServer(socketserver.BaseRequestHandler):  ##必需要繼承這個父類
	def handle(self):  ##必需要從新定義這個方法
		while True:
			try:
				data = self.request.recv(1024)  ##recv就至關於conn了
				if not data: break
				print(data.decode("utf-8"), self.client_address) ##只有data和client_address這兩個量 
				self.request.sendall(data)  
			except Exception:
				break


if __name__ == "__main__":
	s = socketserver.ThreadingTCPServer(('192.168.1.101',9001), MyServer)  ##實例化一個對象
	s.serve_forever()  ##永遠運行

UDP版服務端

import socketserver

class MyServer(socketserver.BaseRequestHandle):
	def handle(self):
        ##(b'ad', <socket.socket fd=444, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0,
        ##   laddr=('192.168.1.101', 8081)>)  request是一個元組
		print(self.request[0].decode("utf-8"))
		self.request[1].sendto(self.request[0], self.client_address)

if __name__ == "__main__":
	s = socketserver.ThreadingUDPServer(('192.168.1.101',8080), MyServer)
	s.server_forever()
相關文章
相關標籤/搜索