getaddrinfo學習

使用socket時,會使用socket.socket(socket.AF_INET,socket.SOCK.DGRAM)來建立一個socket。而後用其來綁定(bind((IP,Port)))或者鏈接,那麼,參數AF_INET和DGRAM是什麼意思呢,是否還有其它的參數呢?python

這裏能夠看到,咱們使用了4個參數:socket.AF_INET,socket.SOCK.DGRAM,IP,Port;可是在建立socket的時候有三個參數,只不過默認參數被忽略了。服務器

首先,地址族是最重要的參數,機器可能接觸不一樣類型的網絡,對地址族的指定就決定了想要進行通訊的網絡類型。在學習的時候,一直使用的是socket.AF_INET即IP地址族。按照tcp/ip層分類,這個參數指的是IP層的協議,固然,不止有ip協議,還有藍牙協議,wifi協議等等網絡

第二個就是套接字類型,這裏使用的是socket.SOCK.DGRAM,固然,在IP協議之上有tcp何udp之分,那麼在其餘ip層協議之上,咱們該怎麼辦呢?這裏設計者使用的是普遍可使用的接口參數,例如:這裏使用的socket.SOCK.DGRAM表明使用套接字數據報文傳輸,在IP協議上就是指UDP,而socket.STREAM指的是可靠的傳輸,在IP上指的是TCP。那麼在其餘協議之上的傳輸也可使用。socket

第三個參數是協議類型,好比上面例子中socket.socket(socket.AF_INET,socket.SOCK.DGRAM)就是指在ip協議中選擇基於數據報的傳輸模式協議,那麼在ip協議中就是指的是UDP,不須要再輸入協議類型了,若是真要輸入協議類型,UDP是17,TCP是6,固然通常默認是0,就是讓下層程序自動挑選,反正通過前面兩個參數能夠指定了,後面這個通常默認便可。tcp

綁定(bind((IP,Port)))或者鏈接的時候呢,就是ip地址和端口,沒啥多說的。ide

那麼在實際使用中,咱們使用getaddrinfo來獲取相關的信息。函數

getaddrinfo函數的參數:socket.getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]])學習

首先,host是想要獲取的ip地址或者能夠經過hosts or DNS解析的字符串,port就是端口(int)或者能夠經過/etc/services解析的字符串,第三個是網絡層的協議類型,第四個就是傳輸層的協議類型,第五個就是經過第三個參數和第四個參數肯定的協議中的哪個,通常來講只有一個,因此通常默認爲0便可,最後一個就是一些設置參數。設計

返回值[(family, socktype, proto, canonname, sockaddr)]3d

首先就是網絡層協議,而後是傳輸層協議,而後是協議編號,canonname不清楚了,sockaddr就是一個元組例如:('220.181.38.148', 80);包含了ip和端口。

clipboard

當使用getaddrinfo獲取信息的時候,最後一個參數很是有用,

首先,若是指定ip地址爲None,那麼其意義就是獲取本地信息,最後一個參數使用socket.AI_PASSIVE就能夠獲取本地全部能夠鏈接的指定端口的信息,

clipboard

若是指定了一個ip,那麼久應該取消socket.AI_PASSIVE這個參數,設置爲0或者其餘參數便可。

還有一個是AI_ADDRCONFIG,這個參數能夠把IP地址上沒法鏈接的地址都過濾掉。例如,若是本機只能使用ipv4來鏈接,而對方能夠接收ipv4和ipv6,那麼這個參數會將ipv6的地址都去掉。

AI_V4MAPPED能夠將ipv4的地址轉換成ipv6的地址,例如,咱們想鏈接的服務只支持ipv4,可是本機只能使用ipv6,那麼使用這個參數能夠將v4轉化爲v6。

參數使用舉例:

當我想鏈接百度的HTTP端口:

from pprint import pprint import socket if __name__== '__main__': infolist = socket.getaddrinfo('baidu.com','www',0,socket.SOCK_STREAM,0,socket.AI_ADDRCONFIG | socket.AI_V4MAPPED) pprint((infolist))

[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 0, '', ('39.156.69.79', 80)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 0, '', ('220.181.38.148', 80))]

當咱們知道ip和端口後,可是咱們不知道這個主機的DNS真實名稱,好比,我被告知192.168.10.22時百度的地址,端口是80,而後我也這樣訪問了,能夠鏈接上,可是這個ip地址是內網地址,是總所周知的,可是,服務器方能夠另DNS返回任意他們想要返回的結果,這裏他們就可讓DNS返回百度的真實名稱給客戶端,致使客戶端認爲這就是百度。

使用這個方式的辦法是使用AI_CANONNAME參數,以下:

clipboard

這種使用只有在服務器定義了反向主機名的時候纔有用,可是在互聯網上,不多有服務器提供了反向主機名,因此須要用其它方式來確認。

其它參數:

clipboard

使用例子:

import socket,sys,argparse def connect_to(hostname_or_ip): try: infolist = socket.getaddrinfo(hostname_or_ip,'www',0,socket.SOCK_STREAM,0,socket.AI_ADDRCONFIG | socket.AI_V4MAPPED | socket.AI_CANONNAME) except socket.gaierror as why: print('Name Service failure:',why.args[1]) sys.exit(1) info = infolist[0] socket_args = info[0:3] address = info[4] s = socket.socket(*socket_args) try: s.connect(address) except socket.error as why: print('Network failure:',why.args[1]) else: print(F'Success:host {address[0]} is listening on port 80.') if __name__ == '__main__': parse = argparse.ArgumentParser(description='Try connect to port 80') parse.add_argument('hostname',help='hostname you want to connect') connect_to(parse.parse_args().hostname)

python tcp_sixteen.py baidu.com Success:host 220.181.38.148 is listening on port 80. python tcp_sixteen.py 220.181.38.148 Success:host 220.181.38.148 is listening on port 80.

相關文章
相關標籤/搜索