日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

socket层内容详解二

發(fā)布時間:2025/4/16 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket层内容详解二 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

tcp協(xié)議
三次握手和四次揮手
所有的斷開都是單方面的

粘包現(xiàn)象
為什么會出現(xiàn)粘包現(xiàn)象:
本質:接收端不知道發(fā)送端發(fā)送的數(shù)據(jù)長度是多少
tcp協(xié)議本身的特點導致:
流式傳輸、無邊界
合包機制
緩存機制
拆包機制
如何解決這個問題?
自定義協(xié)議:先發(fā)送要傳遞數(shù)據(jù)的長度


用戶登錄驗證協(xié)議

# 先創(chuàng)建一個文件,里面隨便放幾個用戶名及對應的密文密碼,如:# userinfoalex|802165d8e6a7e0fbf11b0feca3913271 jane|48610593feea04340acf643a71621e32

?

# server.pyimport socket import json import hashlibsk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen()def get_md5_code(usr, pwd):md5 = hashlib.md5(usr.encode())md5.update(pwd.encode())return md5.hexdigest()conn,addr = sk.accept() msg = conn.recv(1024).decode() dic_msg = json.loads(msg)with open("userinfo") as f:for line in f:usr,pwd = line.strip().split("|")if usr == dic_msg["username"] and pwd == get_md5_code(dic_msg["username"], dic_msg["password"]):ret = {"code": 1}res_msg = json.dumps(ret).encode()conn.send(res_msg)breakelse:ret = {"code": 0}res_msg = json.dumps(ret).encode()conn.send(res_msg)conn.close() sk.close()

?

# client.pyimport socket import jsonusr = input("username: ") pwd = input("password: ") dic = {"username": usr, "password": pwd, "operate": "login"} json_dic = json.dumps(dic) bytes_msg = json_dic.encode()sk = socket.socket() sk.connect(("127.0.0.1", 8080)) # 為什么要放在這里? # 因為比如有的用戶打開客戶端要輸入用戶名的時候想了半天還沒想出來 # 但是客戶端卻一直開著,所以為了避免這種情況把上面兩行放這里 sk.send(bytes_msg)recv_msg = sk.recv(1024).decode() print(recv_msg) dic_code = json.loads(recv_msg) if dic_code["code"]:print("登錄成功") else:print("登錄失敗")sk.close()

?

?

如何讓服務端與一個客戶端聊天時,客戶端退出后,服務端還能繼續(xù)等待下一個客戶端聊天?

# server.pyimport socketsk = socket.socket()sk.bind(('127.0.0.1',9000)) sk.listen()while True: # 和n個人聊天conn,addr = sk.accept()while True: # 和一個人聊n久send_msg = input('msg : ')conn.send(send_msg.encode())if send_msg == 'q':breakmsg = conn.recv(1024).decode()if msg == 'q':breakprint(msg)conn.close()sk.close()

?

# client.pyimport socketsk = socket.socket() sk.connect(('127.0.0.1',9000))while True:msg = sk.recv(1024).decode()if msg == 'q':breakprint(msg)send_msg = input('msg : ')sk.send(send_msg.encode())if send_msg == 'q':breaksk.close()# 這里注意,客戶端輸入q退出程序后,會發(fā)現(xiàn)服務端沒有關閉 # 這時只需重啟客戶端,相當于另一個用戶進入,就可繼續(xù)聊天

?

?

完成一個上傳文件的程序

?

# server.pyimport struct import socket import jsonsk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen()conn, addr = sk.accept() bytes_len = conn.recv(4) info_len = struct.unpack("i", bytes_len)[0] json_info = conn.recv(info_len).decode() info_dic = json.loads(json_info) print(info_dic) with open(info_dic["filename"], "wb") as f:# 這里有問題,如果把這里的1024和client.py里的1024都改為2048# 發(fā)現(xiàn)運行結果后查看該文件時文件大小變小了# 原因是這里雖然寫conn.recv(1024)# 但不代表每次一定就能接收到1024個字節(jié)# 比如最后還剩800多字節(jié),就會發(fā)生拆包現(xiàn)象# 也就是說,雖然寫的是接收1024,其實最后只有800多# 這樣就要把info_dic["filesize"] -= 1024的1024改掉while info_dic["filesize"]:content = conn.recv(1024)f.write(content)# info_dic["filesize"] -= 1024info_dic["filesize"] -= len(content)conn.close() sk.close()

?

import socket import os import json import struct# 在上傳之前,應該先向server端傳遞文件信息:文件大小、文件名、以及要做的操作,這里就要用到send,但是可能會與下面的send產生粘包現(xiàn)象 # 打開文件 # 讀文件 # 按照字節(jié)讀取 # 5000:1024 1024 1024 1024 904 # 這樣就要send五次,不用擔心粘包,只要傳過去5000字節(jié)就行# server端先接收4字節(jié) # 然后直到了文件信息的長度 # 按照長度接收文件信息 # 從文件信息中得到文件的大小 # 就知道要收取的文件的大小是多少 # 開始接收,直到收完文件大小這么多的數(shù)據(jù)# 在上傳之前,應該先向server端傳遞文件信息:文件大小、文件名、以及要做的操作 file_path = r"G:\day01 視頻以及筆記\20181219_163449.mp4" # input("file_path") filesize = os.path.getsize(file_path) filename = os.path.basename(file_path) # print(filesize) # 143799118 # print(filename) # 20181219_163449.mp4 file_info = {"filesize": filesize, "filename": filename, "operate": "upload"} json_info = json.dumps(file_info) file_info_bytes = json_info.encode() bytes_len = len(file_info_bytes) sk = socket.socket() sk.connect(("127.0.0.1", 8080)) # 先發(fā)送文件信息的長度,再發(fā)送文件信息 sk.send(struct.pack("i", bytes_len)) sk.send(file_info_bytes)with open(file_path, "rb") as f:while filesize > 0:content = f.read(1024)filesize -= len(content)sk.send(content) # 這里已經是bytes類型 sk.close()

?

?

udp協(xié)議

?

# server.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1", 8080)) # 這里不用寫sk.listen(),沒有三次握手 msg, client_addr = sk.recvfrom(1024) print(msg) sk.sendto(b"hello", client_addr)sk.close()

?

# client.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM)sk.sendto(b"hello", ("127.0.0.1", 8080)) msg, addr = sk.recvfrom(1024) print(msg)sk.close()

?

?

使用udp協(xié)議實現(xiàn)多人聊天功能

?

# server.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1", 8080)) # 這里不用寫sk.listen(),沒有三次握手# 和一個人多次聊天 while True:msg, client_addr = sk.recvfrom(1024)print(msg.decode())content = input(">>>")sk.sendto(content.encode(), client_addr)sk.close()

?

# client.pyimport socketsk = socket.socket(type=socket.SOCK_DGRAM)# 和一個人多次聊天 # 可以再創(chuàng)建一個client.py,內容一模一樣,然后依次運行 # 都可以跟server端聊天 while True:content = input(">>>")# 多人聊天時,可以這樣區(qū)分誰跟server端聊天content = "%s : %s" % ("alex", content)sk.sendto(content.encode(), ("127.0.0.1", 8080))msg, addr = sk.recvfrom(1024)print(msg.decode())sk.close()

?

?

使用socketserver來實現(xiàn)tcp協(xié)議socket的并發(fā)

?

# server.pyimport socketserver# socketserver是一個上層模塊,socket是一個下層模塊 # 因此 socketserver 可以實現(xiàn)并發(fā) # socket是基于socketserver的下層模塊 # socketserver是基于socket的上層模塊class Myserver(socketserver.BaseRequestHandler):# 注意:有多少個client端,就相當于這里有多少個handledef handle(self):# print("連接上了")# print(self.request) # 這個就是conn,因此這樣寫# conn = self.request# conn.send(b"hello")# 可以與多個client端運行,即并發(fā)while True:conn = self.requestconn.send(b"hello")# 這里不需要實例化一個對象 server = socketserver.ThreadingTCPServer(("127.0.0.1", 8080), Myserver) server.serve_forever()

# socketserver
# 寫一個socketserver端,能夠同時和多個client端進行通信,互不干擾
# socketserverserver端不能用input,因為server端使用了并發(fā)機制
# 如何寫?
# 1.必須繼承socketserver.BaseRequestHandler
# 2.自定義一個類必須實現(xiàn)handler方法
# 3.自定義類中的self.request就是conn
# 4.啟動程序的方法:
# socket.server.ThreadingTCPServer(ip, port),自定義類名)
# obj.server_forever()

?

# client.pyimport socketsk = socket.socket() sk.connect(("127.0.0.1", 8080))while True:msg = sk.recv(1024)print(msg)sk.close()

?

# client1.pyimport socketsk = socket.socket() sk.connect(("127.0.0.1", 8080))while True:msg = sk.recv(1024)print(msg)sk.close()

?

# client2.pyimport socketsk = socket.socket() sk.connect(("127.0.0.1", 8080))while True:msg = sk.recv(1024)print(msg)sk.close()

?

1.兩個連續(xù)的send會產生粘包現(xiàn)象 2.用struct自定義協(xié)議可以解決粘包問題 3.文件傳輸時不用考慮粘包問題 4.自定義協(xié)議的進階版本:先發(fā)送字符串的長度,再發(fā)送字符串先發(fā)送json的長度,再發(fā)送jsonjson的字典中包含著下一條信息的長度,然后按照長度接收tcp與udp協(xié)議 tcp面向連接的,可靠的,全雙工的,流式傳輸面向連接:同一時刻只能和一個客戶端通信要進行三次握手,四次揮手可靠的:數(shù)據(jù)不丟失、慢全雙工:能夠雙向通信(send,recv誰先都可以)流式傳輸:粘包、無邊界 udp無連接的,面向數(shù)據(jù)包,不可靠的,快速無連接的:不需要accept/connect, 也沒有握手面向數(shù)據(jù)包的:不會粘包不可靠的:沒有自動回復的機制快速的:沒有復雜的計算、保證數(shù)據(jù)傳輸?shù)臋C制

?

# 再講 pickle 模塊import pickleclass Course:def __init__(self, name, period, price, teacher):self.name = nameself.period = periodself.price = priceself.teacher = teacherpython = Course("python", "six months", 19800, "alex") linux = Course("linux", "five months", 15800, "taibai") with open("course_info", "wb") as f:pickle.dump(python, f)pickle.dump(linux, f)with open("course_info", "rb") as f:while 1:try:# print(pickle.load(f).name) # python linuxobj = pickle.load(f)print(obj.__dict__)# {'name': 'python', 'period': 'six months',# 'price': 19800, 'teacher': 'alex'}# {'name': 'linux', 'period': 'five months',# 'price': 15800, 'teacher': 'taibai'}except EOFError:break# dumps str-->bytes # dump str-->文件里的bytes# 1.json能處理的數(shù)據(jù)類型有限,但是所有語言通用 # pickle支持python中幾乎所有對象,但是只能在python語言中使用 # 2.json的dumps的結果是str, pickle的dumps的結果是bytes # 3.json不能連續(xù)dump多個數(shù)據(jù)到文件中 # pickle可以

?

# 驗證客戶端的合法性# server.pyimport os import hmac import socketdef auth(conn):secret_key = b"jane"rand_b = os.urandom(32)conn.send(rand_b)obj = hmac.new(secret_key, rand_b)res1 = obj.digest()res2 = conn.recv(1024)cmp_res = hmac.compare_digest(res1, res2)return cmp_ressk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.listen()conn, addr = sk.accept() res = auth(conn) if res:print("是合法的客戶端")conn.send("您好".encode()) else:conn.close()sk.close()# 運行后得出結果:是合法的客戶端 # 只要兩邊的secret_key的值不一樣,服務端就知道不是合法的客戶端

?

# client.pyimport socket import hmacdef auth(sk):secret_key = b"jane"rand_b = sk.recv(32) # 注意這里obj = hmac.new(secret_key, rand_b)res2 = obj.digest()sk.send(res2)sk = socket.socket() sk.connect(("127.0.0.1", 8080)) auth(sk) msg = sk.recv(1024) print(msg.decode())sk.close()

?

轉載于:https://www.cnblogs.com/shawnhuang/p/10309089.html

《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的socket层内容详解二的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。