一、說明
上篇文章我們講解了通過廣播的方式,客戶端可以拿到服務端的 IP 地址和端口號,但是廣播是基于 UDP 的不可靠鏈接,不能保證兩端通訊的安全和可靠性,所以我們要建立一條安全通訊通道,這里我們使用socket 在兩端建立長連接用于傳輸數(shù)據(jù)。至于長連接的概念請大家自行百度
二、完整案例
1、客戶端:由于客戶端已經(jīng)通過局域網(wǎng)廣播拿到到我們要連接的IP 和端口,直接建立連接就可以了,當然了前提是服務端的服務已經(jīng)啟動成功
import socket, os
import threading
import time
import json
# TODO:要連接的電腦ip和端口號
server_ip_port = (‘IP’, 1241)
BUF_SIZE = 4096
class SocketClient:
def __init__(self):
self.socket = None
self.emptyDataCount = 0
# 連接服務端
def connectServer(self) -> bool:
global server_ip_port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
self.socket.connect(server_ip_port)
print('socket服務連接成功')
self.listenMsg()
#發(fā)送心跳
self.sendHeartbeatThread()
except Exception as e:
print(e)
code = e.args[0]
print('code == ',code)
if code == 61:
print('請先啟動服務端服務,再啟動客戶端服務')
elif code == 60:
print('連接超時,請檢查您填寫的ip地址和端口號是否正確')
elif code == 51:
print('沒有網(wǎng)絡')
self.socket.close()
print('socket服務連接失敗')
return False
return True
# 監(jiān)聽消息
def listenMsg(self):
thread = threading.Thread(target=self.receiveMsg)
thread.setDaemon(1)
thread.start()
def receiveMsg(self):
while True:
data = self.socket.recv(BUF_SIZE).decode('utf-8') # 接受信息
if str(data) == '':
self.emptyDataCount += 1
if self.emptyDataCount >= 10:
self.emptyDataCount = 0
# 關閉socket
self.socket.close()
print('服務器斷開了連接')
break
print('data:', str(data))
def sendMsg(self, msg):
try:
self.socket.send(msg.encode('utf-8'))
except Exception as e:
pass
else:
pass
def close(self):
self.socket.close()
def sendHeartbeatThread(self):
thread = threading.Thread(target=self.sendHeartbeat)
thread.setDaemon(1)
thread.start()
#發(fā)送心跳
def sendHeartbeat(self):
# send heart_beat
while True:
host_name = socket.gethostname()
data_to_server = {'type':1000 ,'status': 'alive'}
data_dumped = json.dumps(data_to_server)
try:
self.socket.send(data_dumped.encode('utf-8'))
except socket.error:
print("Send failed!!")
print("==========本客戶端已脫機========")
print('I - ', os.getpid(), '- am alive.')
time.sleep(1)
2、服務端:服務端要檢測和維護哪些客戶端的長連接已經(jīng)鏈接成功,接收和發(fā)送消息以及異常狀態(tài)
import socketserver
from Tools import SocketTools as tools
BUF_SIZE = 4096
class MyTCPHandler(socketserver.BaseRequestHandler):
def setup(self):
print("before handle,連接建立:",self.client_address)
def finish(self):
print("finish run after handle")
def handle(self):
try:
while True:
self.data=self.request.recv(BUF_SIZE)
print("{} send:".format(self.client_address),self.data)
if not self.data:
print("connection lost")
break
self.request.sendall(self.data.upper())
except Exception as e:
print(self.client_address,"連接斷開")
finally:
self.request.close()
HOST,PORT = tools.get_host_ip(),1241
server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler) #多線程版
server.serve_forever()
3.獲取本機 IP
import socket
import requests
# 獲取本機IP
def get_host_ip():
ip = ''
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
except Exception as e:
code = e.args[0]
if code == 51:
print('獲取IP失敗=============沒有網(wǎng)絡=============')
finally:
s.close()
return ip
# 網(wǎng)絡檢測
def isConnected():
try:
html = requests.get("http://www.baidu.com",timeout=2)
except:
return False
return True
通過以上的方式我們就可以通過 socket 建立一個基于 TCP 的可靠網(wǎng)絡傳輸服務,就可以實現(xiàn)局域網(wǎng)消息互發(fā)了,當然了我們也可以在此基礎上實現(xiàn)局域網(wǎng)的聊天軟件,有興趣的小伙伴可以研究下
三、總結
剛開始我們基于客戶端的方式,在服務端起了多個線程用來檢測鏈接進來的客戶端,同樣也實現(xiàn)了 我們想要的功能,但是線程服務需要我們自己維護,無法實現(xiàn)多客戶端自動并發(fā),沒有充分利用資源,于是我們就用了 socketserver 模塊建立服務替代了客戶端起多個服務的方式,維護起來更方便。