5.2黑客成長(zhǎng)日記——socket-1

reference

1.0 黑客成長(zhǎng)日記
2.0入門網(wǎng)絡(luò)工具
3.0 工具合集
4.0知乎
5.0客戶端服務(wù)端

其實(shí)不黑客

Socket編程

Socket編程所牽涉的東西非常寬泛,調(diào)用各種編程語(yǔ)言對(duì)socket的TCP(TCP可靠通信的實(shí)現(xiàn)方式)和UDP封裝進(jìn)行網(wǎng)絡(luò)通信,可以是監(jiān)聽外部鏈接,也可以是主動(dòng)發(fā)起鏈接請(qǐng)求,發(fā)送特定協(xié)議并進(jìn)行通信,如何制定協(xié)議規(guī)范,如何進(jìn)行協(xié)議的編碼和解碼,如何將協(xié)議數(shù)據(jù)轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)上和從網(wǎng)絡(luò)接收辨別且處理成功(牽涉到TCP粘包等問題),如何針對(duì)建立的鏈接進(jìn)行管理等。。。。

前置知識(shí):

1. 什么是socket
IP+端口,socket編程就是承載通訊的系統(tǒng)資源標(biāo)識(shí)
白話Socket 就是插座 端口就是插座上的孔 端口不能被其他進(jìn)程占用 抽象理解 Socket 類似于操作某個(gè)IP地址上的某個(gè)端口達(dá)到點(diǎn)對(duì)點(diǎn)通信的目的, 需要綁定到某個(gè)具體的進(jìn)程中和端口中。

2.TCP/IP四層協(xié)議

1.jpg

3.半相關(guān):網(wǎng)絡(luò)中用一個(gè)三元組可以在全局唯一標(biāo)志一個(gè)進(jìn)程:
(協(xié)議,本地地址,本地端口號(hào))

4.全相關(guān)五元組:一個(gè)完整的網(wǎng)間進(jìn)程通信需要兩個(gè)進(jìn)程,同一種協(xié)議
(協(xié)議,本地地址,本地端口號(hào),遠(yuǎn)地地址,遠(yuǎn)地端口號(hào))

開始有點(diǎn)硬核起來(lái):客戶端編程

socket類提供標(biāo)準(zhǔn)的BSD Socket API。
為了方便網(wǎng)絡(luò)服務(wù)器的開發(fā),**socketserver **為服務(wù)器端編程提供了進(jìn)一步封裝
調(diào)用socket.socket可以創(chuàng)建一個(gè)Socket實(shí)例,socket類構(gòu)造函數(shù)聲明如下:

socket(family, type[,protocal])
我們看到socket構(gòu)造函數(shù)接收三個(gè)參數(shù),第一個(gè)為family。family表示套接字對(duì)象使用的地址族,可選值:AF_INET——IPv4地址族,AF_INET6——IPv6地址族,AF_UNIX——針對(duì)類UNIX系統(tǒng)的套接字。第二個(gè)為type,可使用的類型如下:socket.SOCK_STREAM基于TCP的流式socket通信
socket.SOCK_DGRAM基于UDP的數(shù)據(jù)報(bào)式socket通信

# -*- coding: UTF-8 -*-
import socket
import sys

#測(cè)試類
class Client:
    def __init__(self, host, ip=None, port=80):
        self.host = host #待連接的遠(yuǎn)程主機(jī)的域名
        self.ip = ip
        self.port = port
    #
    #
    def connet_test(self): #連接方法
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            print("TCP connet")
        except socket.error as e:
            print("Failed to create socket. Error: %s"%e)
        sys.exit() #退出進(jìn)程
    #
    #
    def connet(self): #連接方法
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            print('socket created')
        except socket.error as e:
            print("Failed to create socket. Error: %s"%e)
            sys.exit() #退出進(jìn)程
        try:
            remote_ip = self.ip or socket.gethostbyname(self.host)#根據(jù)域名獲取ip
            print("get domain by IP: ", remote_ip)
        except socket.gaierror as e:
            print('主機(jī)無(wú)法被解析:', e)
            sys.exit() #退出進(jìn)程
        try:
            s.connect((remote_ip, self.port)) #連接
            print('socket連接成功')
            message = bytes("GET / HTTP/1.1\r\n\r\n", encoding="UTF-8")
            # message = b"GET / HTTP/1.1\r\n\r\n"
            s.sendall(message) #發(fā)送數(shù)據(jù)
            print('發(fā)送數(shù)據(jù)成功')
            reply = s.recv(4096) #接收數(shù)據(jù)
            # while True:
            #     reply = s.recv(4096)
            #     if reply:
            #         print(reply)
            #     else:
            #         s.close()
            #         break
            print(reply)
            s.close() #關(guān)閉連接
        except socket.error:
            print("socket error")
            print('發(fā)送數(shù)據(jù)失敗')
            sys.exit() #退出進(jìn)程
        return reply

if __name__ == '__main__':
    cl = Client('www.baidu.com', '127.0.0.1', 8008)
    # cl = Client('www.woqunidaye.com')
    # cl = Client('www.baidu.com')
    reply = cl.connet()
    print(reply.decode('UTF-8'))

上面的代碼里,總共有三個(gè)try,第一個(gè)try,新建socket.socket實(shí)例,構(gòu)造一個(gè)半相關(guān)(這個(gè)是我自己想的不一定對(duì)),這里你把網(wǎng)給停了,這一步依舊是能走通的。
第二個(gè)try,通過(guò)域名獲取ip,如果是一個(gè)沒用的域名,就獲取不到ip
第三個(gè)try,選擇要接通的端口,這里是80,通過(guò)s.connect方法鏈接遠(yuǎn)程主機(jī)。連接建立后,通過(guò)sendall發(fā)送數(shù)據(jù),recv接收數(shù)據(jù)。注意數(shù)據(jù)是二進(jìn)制的(如果數(shù)據(jù)很多的時(shí)候需要使用send方法循環(huán)發(fā)送,接收也一樣,數(shù)據(jù)很大或者不知道具體多大就需要循環(huán)接收。)
ps: b"GET / HTTP/1.1\r\n\r\n"就是標(biāo)準(zhǔn)的HTTP 1.0的請(qǐng)求報(bào)文

while True:
    reply = s.recv(4096)
    if reply:
        print(reply)
    else:
        s.close()
        break

開始有點(diǎn)硬核起來(lái):服務(wù)端編程


# -*- coding: UTF-8 -*-
import socket
import sys

class server:
    def __init__(self, ip, port):
        self.port=port
        self.ip=ip

    def start(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#創(chuàng)建socket
        try:
            s.bind((self.ip, self.port)) # 綁定
            # 把socket綁定到傳入的IP和端口上,調(diào)用bind方法,傳入ip和端口號(hào)。
            s.listen(10)#監(jiān)聽
            print('socket正在監(jiān)聽')
            # 進(jìn)入監(jiān)聽狀態(tài),listen方法接收一個(gè)參數(shù),用來(lái)指定可以同時(shí)掛起的連接數(shù)。
            print("s.family", s.family)
            print('等待客戶端連接')
            conn, addr = s.accept() # 接收連接
            # accept方法會(huì)返回一個(gè)代表當(dāng)前鏈接的connection對(duì)象和客戶端的ip地址
            print('客戶端連接 ' + addr[0] + ':' + str(addr[1]))
            data = conn.recv(1024) # 接收數(shù)據(jù)
            print("客戶端數(shù)據(jù):%s" % data)
            conn.sendall(bytes("你好客戶端\n\r", encoding = "utf8")) # 發(fā)送數(shù)據(jù)
            # conn.sendall(bytes("你好客戶端\n\r", encoding = "utf8")) # 發(fā)送數(shù)據(jù)
            print("服務(wù)端消息發(fā)往客戶端成功")
            conn.close()#關(guān)閉連接
        except socket.error as e:
            print(e)
            sys.exit()
        # finally:
        #      s.close() #關(guān)閉服務(wù)端
        print("正常關(guān)閉服務(wù)器")
        s.close() #關(guān)閉服務(wù)端


if __name__ == '__main__':
    s = server('', 8008)
    s.start()

server類的start方法創(chuàng)建了一個(gè)簡(jiǎn)單的服務(wù)端。和客戶端編程類似,首先創(chuàng)建一個(gè)socket對(duì)象。隨后,我們要把socket綁定到傳入的IP和端口上,調(diào)用bind方法,傳入ip和端口號(hào)。服務(wù)端不會(huì)主動(dòng)連接其他主機(jī),而是等待客戶端連接,這需要進(jìn)入監(jiān)聽狀態(tài),listen方法接收一個(gè)參數(shù),用來(lái)指定可以同時(shí)掛起的連接數(shù)。監(jiān)聽模式之后,如果有客戶端連接進(jìn)來(lái),如何接收連接呢?需要使用accept方法。accept方法會(huì)返回一個(gè)代表當(dāng)前鏈接的connection對(duì)象和客戶端的ip地址。接下來(lái)就可以使用conn對(duì)象來(lái)接收和發(fā)送數(shù)據(jù)了,最后調(diào)用conn.close()關(guān)閉和客戶端的連接。下面我們啟動(dòng)服務(wù)端,然后再命令行啟動(dòng)nc,來(lái)連接服務(wù)端。

一個(gè)有趣的現(xiàn)象:在客戶端s.recv(4096),在服務(wù)器端s.recv(1024),答:顯然是自己設(shè)定的,sb
ps:把基于TCP改成基于UDP的時(shí)候本地不支持,不知道為啥,坑,待填
pps:cli能收到server的回復(fù),可是馬上就報(bào)錯(cuò)了, 不知道為啥,后面不知道為啥又不報(bào)錯(cuò)了。。。

socket正在監(jiān)聽
s.family AddressFamily.AF_INET
等待客戶端連接
客戶端連接 127.0.0.1:13819
客戶端數(shù)據(jù):b'GET / HTTP/1.1\r\n\r\n'
服務(wù)端消息發(fā)往客戶端成功
[WinError 10022] 提供了一個(gè)無(wú)效的參數(shù)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容