socket使用

本節導讀:python

  • 什麼是socket
  • socket通訊討論
  • socket套接字方法
  • socket服務端客戶端的建立

一 什麼是scoketlinux

Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有。shell

socket起源於Unix,而Unix/Linux 基本哲學之一就是「一切皆文件」,均可以用「打開open –> 讀寫write/read –> 關閉close」模式 來操做。Socket就是該模式的一個實現,socket便是一種特殊的文件,一些socket函數就是對其進行的操做(讀/寫IO、打開、關閉)編程

你想給另外一臺計算機發消息,你知道他的IP地址,他的機器上同時運行着qq、迅雷、word、瀏覽器等程序,你想給他的qq發消息,那想一下,你如今只能經過ip找到他的機器,但若是讓這臺機器知道把消息發給qq程序呢?答案就是經過port,一個機器上能夠有0-65535個端口,你的程序想從網絡上收發數據,就必須綁定一個端口,這樣,遠程發到這個端口上的數據,就全會轉給這個程序設計模式

 

二 socket的工做流程瀏覽器

下面咱們舉個打電話的小例子來講明一下緩存

若是你要給你的一個朋友打電話,先撥號,朋友聽到電話鈴聲後提起電話,這時你和你的朋友就創建起了鏈接,就能夠講話了。等交流結束,掛斷電話結束這次交談。 生活中的場景就解釋了這工做原理。安全

(若是你去一家餐館吃飯,假設哪裏的老闆就是服務端,而你本身就是客戶端,當你去吃飯的時候,你確定的知道那個餐館,也就是服務端的地址吧,可是對於你本身來講,餐館的老闆不須要知道你的地址吧)服務器

 

三 socket套接字方法網絡

socket參數

family(socket家族)

  • socket.AF_UNIX:用於本機進程間通信,爲了保證程序安全,兩個獨立的程序(進程)間是不能互相訪問彼此的內存的,但爲了實現進程間的通信,能夠經過建立一個本地的socket來完成
  • socket.AF_INET:(還有AF_INET6被用於ipv6,還有一些其餘的地址家族,不過,他們要麼是隻用於某個平臺,要麼就是已經被廢棄,或者是不多被使用,或者是根本沒有實現,全部地址家族中,AF_INET是使用最普遍的一個,python支持不少種地址家族,可是因爲咱們只關心網絡編程,因此大部分時候我麼只使用AF_INET)

socket type類型

  • socket.SOCK_STREAM #for tcp
  • socket.SOCK_DGRAM #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 #廢棄了

(Only SOCK_STREAM and SOCK_DGRAM appear to be generally useful.)

proto=0 請忽略,特殊用途

fileno=None 請忽略,特殊用途

 

socket函數

phone.bind('主機ip地址',端口號)  #綁定到(主機,端口號)套接字
phone.listen() #開始TCP監聽
phone.accept() #被動接受TCP客戶的鏈接,等待鏈接的到來
服務端套接字函數
s.connect()  #主動初始化TCP服務器鏈接
s.connect_ex()   #connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
客戶端套接字函數
s.recv()  #接收數據
s.send()# 發送數據(send在待發送數據量大於己端緩存區剩餘空間時,數據丟失,不會發完,可後面經過實例解釋)
s.sendall()  #發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大於己端緩存區剩餘空間時,數據不丟失,循環調用send直到發完)
s.recvfrom()  #Receive data from the socket. The return value is a pair (bytes, address)
s.getpeername()  #鏈接到當前套接字的遠端的地址
s.sendto() #發送UDP數據
s.getsockname() #返回指定套接字的參數
s.setsockopt() #設置指定套接字的參數
s.close()  # 關閉套接字
socket.setblocking(flag) #True or False,設置socket爲非阻塞模式,之後講io異步時會用
socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)   #返回遠程主機的地址信息,例子 socket.getaddrinfo('luffycity.com',80)
socket.getfqdn()# 拿到本機的主機名
socket.gethostbyname()  # 經過域名解析ip地址
公共套接字函數
phone.setblocking()  #設置套接字的阻塞與非阻塞模式
phone.settimeout()  #設置阻塞套接字操做的超時時間
phone.gettimeout()  #獲得阻塞套接字操做的超時時間
面向鎖的套接字函數
phone.fileno()  # 套接字的文件描述符
phone.makefile() #建立一個與該套接字相關的文件
面向文件的套接字函數

四 socket 服務端,客戶端的建立

socket服務端建立

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#買手機
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #能夠屢次啓動
#執行屢次的時候會報錯,那麼怎麼辦呢、?就在綁卡前面加上上面那句setsockopt方法就ok了
phone.bind(('192.168.20.44',8080))#綁定手機卡(ip,端口)
# 端口號在1024之前的是系統用的,1024之後的都是你本身寫的程序去定義的端口

print('starting run......')
phone.listen(5) #開機   5表明的是最多掛起5個,也能夠好多個
while True: #連接循環
    coon,client_addr=phone.accept()#等待接電話,(coon是創建的連接,客戶端的ip和端口號組成的元組)
    print(coon,client_addr)

    #收發消息
    while True:  #通訊循環
        try:  #若是不加try...except ,就會報錯,由於它不知道你何時斷開連接的,服務器還覺得你在運行
            data = coon.recv(1024) #收了1024個字節的消息
            print('client data 收到消息:%s'%data.decode('utf-8'))
            coon.send(data.upper())  #發消息
        except Exception:  #由於你不知道客戶端何時斷開連接,
            break
    coon.close() #掛電話
phone.close() #關機


# 處理邏輯錯誤的兩種方式:
    # if 判斷
    # try...except 異常處理
# 異常處理
# 當你知道直接錯誤的條件時就用if判斷了
# 當程序錯誤必定發生,可是你又預知不了它出錯的條件是什麼的時候,就用try...except

服務端
服務端

socket客戶端建立

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#買手機
phone.connect(('192.168.20.44',8080))  #直接鏈接服務端的ip和端口

# 發收消息
while True:
    msg = input('>>:').strip()  #用戶輸入
    if not msg:continue  #若是爲空就繼續輸
    phone.send(msg.encode('utf-8'))  #  發送你輸入的消息
    # phone.send('hello'.encode('utf-8'))
    data = phone.recv(1024)  #在接收一下
    print('server back res服務端返回結果:>>%s'%data.decode('utf-8'))

phone.close()
客戶端

注意:

若是你在重啓服務端的時候可能遇到這樣的問題:

這個是因爲你的服務端仍然存在四次揮手的time_wait狀態在佔用地址(若是不懂,請深刻研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.服務器高併發狀況下會有大量的time_wait狀態的優化方法)。那麼怎麼解決呢?你也能夠這樣的

1 #加入一條socket配置,重用ip和端口
2 
3 phone=socket(AF_INET,SOCK_STREAM)
4 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
5 phone.bind(('127.0.0.1',8080))

基於tcp協議模擬ssh遠程執行命令

import socket
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#買手機
phone.bind(('192.168.20.44',8081))#綁定手機卡
phone.listen(5)#阻塞的最大個數
print('starting....')
while True:
    conn,addr=phone.accept()#等待鏈接
    print(addr,conn)
    while True:
        cmd=conn.recv(10240)#接收的最大值
        # if not cmd :break
        print('接收的是:%s'%cmd.decode('utf-8'))
        #處理過程
        res=subprocess.Popen(cmd.decode('utf-8'),shell=True,   #Popen是執行命令的方法
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE )
        stdout=res.stdout.read()
        stuerr=res.stderr.read()
        conn.send(stdout+stuerr)
    conn.close()
phone.close()
遠程ssh

基於udp協議的socket

from socket import *
udp_server = socket(AF_INET,SOCK_DGRAM)
udp_server.bind(('127.0.0.1',8080)) #綁定
while True:#通信循環
    msg,client_addr= udp_server.recvfrom(1024)
    print('收到的消息是:%s'%msg.decode('utf-8'))
    udp_server.sendto(msg.upper(),client_addr)
udp_server.close()
udp
相關文章
相關標籤/搜索