TCP 的概念
TCP 的英文全拼(Transmission Control Protocol)簡稱傳輸控制協議,它是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議。
socket 的概念
socket (簡稱 套接字) 是程式之間通訊一個工具,程式之間想要進行網路通訊需要基於這個 socket。它負責程式之間的網路資料傳輸,好比資料的搬運工。不誇張的說,只要跟網路相關的應用程式或者軟體都使用到了 socket 。
客戶端
下面是一個開發TCP應用程式客戶端的一般流程,後面見具體程式碼:
建立客戶端套接字物件
和服務端套接字建立連線
傳送資料
接收資料
關閉客戶端套接字
from socket import *
class Cilent_Socket:
def __init__(self):
self.tcp_client_socket = socket(AF_INET, SOCK_STREAM) # AF_INET指ipv4地址,SOCK_STREAM指TCP協議
self.tcp_client_socket.connect(('192.168.137.1',8989)) #連線服務端,指定伺服器ip和埠
def run(self):
while True:
# 使用者輸入資料
send_data = input("我:")
if len(send_data)==0:
print('已斷開連線!')
break
if send_data == "quit" or send_data == "exit" or send_data =='Bye'or send_data =='bye':
self.tcp_client_socket.send(send_data.encode("gbk"))
recv_data = self.tcp_client_socket.recv(4096).decode('gbk')
print('小美:', recv_data)
self.tcp_client_socket.close()
break
self.tcp_client_socket.send(send_data.encode("gbk")) # 接收對方傳送過來的資料,最大接收4096個位元組
recv_data = self.tcp_client_socket.recv(4096).decode('gbk')
print('小美:', recv_data)
# 關閉套接字
self.tcp_client_socket.close()
def main():
client = Cilent_Socket()
client.run()
if __name__ == '__main__':
main()複製程式碼
上面程式碼中的__init__
方法初始化了一個客戶端套接字,並與伺服器建立一個長連線,run()
方法中用於和後臺機器人傳送訊息和接收機器人給你返回的訊息。
服務端
建立一個服務端程式的基本步驟是:
建立服務端端套接字物件
繫結埠號
設定監聽
等待接受客戶端的連線請求
接收資料
傳送資料
關閉套接字
要建立一個能自動回覆的機器人,只要迴圈接收使用者輸入的資訊,將其輸入的關鍵詞進行判斷,可以後臺預先給定它對應的關鍵詞對應給使用者回覆的資訊即可,或者呼叫已知已經做好的API介面。下面兩種情況會分別進行介紹。
1.自定義訊息關鍵詞回覆
from socket import *
import time
import random
class Server_Socket:
def __init__(self):
tcp_server_socket = socket(AF_INET, SOCK_STREAM)
tcp_server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True) #設定埠複用
tcp_server_socket.bind(('', 8989)) # 繫結伺服器埠
tcp_server_socket.listen(128)
self.tcp_server_socket = tcp_server_socket
self.chat_list = ['今天天氣真好,和朋友出去玩一下吧','今天你學習了嗎','又不知道吃什麼','藍瘦香菇','好嗨喲','去看電影吧','去吃好吃的'] # 定義初始化訊息回覆列表
def start(self):
client_socket, client_addr = self.tcp_server_socket.accept()
while True:
# 接收對方傳送過來的資料
recv_data = client_socket.recv(4096).decode('gbk') # 接收4096個位元組
if len(recv_data) == 0:
print("程式結束")
break # 下面一串是對使用者的輸入邏輯進行判斷
elif recv_data =="quit" or recv_data =="exit" or recv_data =='Bye' or recv_data =='bye' or recv_data =='再見':
client_socket.send('再見'.encode('gbk'))
break
elif "你好" in recv_data or "hello" in recv_data:
client_socket.send("你好".encode('gbk'))
elif "sb" in recv_data or "SB" in recv_data or "傻" in recv_data or "二貨" in recv_data :
client_socket.send("你才傻,你全家都傻!!!".encode('gbk'))
elif "賤" in recv_data or "蠢" in recv_data :
client_socket.send("你個蠢貨!".encode('gbk'))
elif "吃" in recv_data or "hello" in recv_data:
client_socket.send("紅燒肉、東坡肘子...".encode('gbk'))
elif "玩" in recv_data or "hello" in recv_data:
client_socket.send("雲南麗江不錯!".encode('gbk'))
elif "名字" in recv_data or "name" in recv_data:
client_socket.send("我叫小美,編號9527,哈哈...".encode('gbk'))
elif "時間" in recv_data or "time" in recv_data:
client_socket.send(('現在時間是:'+time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))).encode('gbk')) # 返回當前時間
else:
self.chat_list.append(recv_data) # 收集使用者輸入資訊,豐富詞彙
rand_idx = random.randint(0, len(self.chat_list) - 1) # 通過隨機下標獲取一條資訊
send_data = self.chat_list[rand_idx] # 將資訊傳送給客戶端
client_socket.send(send_data.encode('gbk')) # 關閉為這個客戶端服務的套接字,只要關閉了,就意味著為不能再為這個客戶端服務了,如果還需要服務,只能再次重新連線
client_socket.close()
def main():
server = Server_Socket()
server.start()
if __name__ == '__main__':
main()複製程式碼
上面的程式碼是聊天機器人服務端程式碼,可和使用者進行一般的閒聊,返回當前時間等,程式碼邏輯不復雜,優點是可以自行定製。
2.呼叫圖靈機器人API實現自動回覆
圖靈機器人的介面可以實現的功能有:中文聊天,情感引擎等。要使用圖靈的API,首先要去它官網進行註冊,然後建立機器人,獲取一個APIkey,然後才能使用它的API介面。下面是網址入口:
下面對它的API文件有用的一部分進行摘抄:
編碼方式
UTF-8(呼叫圖靈API的各個環節的編碼方式均為UTF-8)
介面地址
openapi.tuling123.com/openapi/api…
請求方式
HTTP POST
請求引數
請求引數格式為 json 請求示例:
{ "reqType":0, "perception": { "inputText": { "text": "附近的酒店" }, "inputImage": { "url": "imageUrl" }, "selfInfo": { "location": { "city": "北京", "province": "北京", "street": "資訊路" } } }, "userInfo": { "apiKey": "", "userId": "" }}複製程式碼
引數說明
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
reqType | int | N | - | 輸入型別:0-文字(預設)、1-圖片、2-音訊 |
perception | - | Y | - | 輸入資訊 |
userInfo | - | Y | - | 使用者引數 |
perception
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
inputText | - | N | - | 文字資訊 |
inputImage | - | N | - | 圖片資訊 |
inputMedia | - | N | - | 音訊資訊 |
selfInfo | - | N | - | 客戶端屬性 |
注意:輸入引數必須包含inputText或inputImage或inputMedia!
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
text | String | Y | 1-128字元 | 直接輸入文字 |
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
url | String | Y | 圖片地址 |
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
url | String | Y | 音訊地址 |
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
location | - | N | - | 地理位置資訊 |
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
city | String | Y | - | 所在城市 |
province | String | N | - | 省份 |
street | String | N | - | 街道 |
userInfo
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
apiKey | String | Y | 32位 | 機器人標識 |
userId | String | Y | 長度小於等於32位 | 使用者唯一標識 |
groupId | String | N | 長度小於等於64位 | 群聊唯一標識 |
userIdName | String | N | 長度小於等於64位 | 群內使用者暱稱 |
輸出引數
輸出示例:
{ "intent": { "code": 10005, "intentName": "", "actionName": "", "parameters": { "nearby_place": "酒店" } }, "results": [ { "groupType": 1, "resultType": "url", "values": { "url": "http://m.elong.com/hotel/0101/nlist/#indate=2016-12-10&outdate=2016-12-11&keywords=%E4%BF%A1%E6%81%AF%E8%B7%AF" } }, { "groupType": 1, "resultType": "text", "values": { "text": "親,已幫你找到相關酒店資訊" } } ]}複製程式碼
引數說明
引數 | 型別 | 是否必須 | 取值範圍 | 說明 |
---|---|---|---|---|
intent | - | Y | - | 請求意圖 |
results | - | N | - | 輸出結果集 |
intent
引數 | 型別 | 是否包含 | 取值範圍 | 說明 |
---|---|---|---|---|
code | int | Y | - | 輸出功能code |
intentName | String | N | - | 意圖名稱 |
actionName | String | N | - | 意圖動作名稱 |
parameters | Map | N | - | 功能相關引數 |
results
引數 | 型別 | 是否包含 | 取值範圍 | 說明 |
---|---|---|---|---|
resultType | String | Y | 文字(text);連線(url);音訊(voice);視訊(video);圖片(image);圖文(news) | 輸出型別 |
values | - | Y | - | 輸出值 |
groupType | int | Y | - | ‘組’編號:0為獨立輸出,大於0時可能包含同組相關內容 (如:音訊與文字為一組時說明內容一致) |
下面是針對文件來封裝實現輸入關鍵詞來返回使用者輸入資訊的函式程式碼:
import requests
import json
def get_response(msg):
api = 'http://openapi.tuling123.com/openapi/api/v2' # 介面地址
data = { "perception": { "inputText": { "text": msg }, "inputImage": { "url": "imageUrl" }, "selfInfo": { "location": { "city": "成都", # 引數必須指定地點 "province": "四川", # 引數必須 "street": "蜀西路" } } }, "userInfo": { "apiKey": '', # 引數必須此處填入網站申請的key "userId": "" } }
data = json.dumps(data) # 將字典格式轉化為json格式,另外loads函式是將json轉化為python中的字典
print(data)
print('=================================================================================')
r = requests.post(api, data=data).json() # 將post請求的結果轉為json
print(r)
return r['results'][0]['values']['text'] # 返回的資料
mes = get_response('天氣') # 輸入關鍵詞複製程式碼
上面用到了python內建的request和json庫,呼叫了幾次發現有時返回的結果不太滿意,不知道是不是沒有買它套餐的原因。上一個版本的機器人服務端只實現了單使用者,下面實現可以多使用者聊天的版本:
import socket
import threading
import requests
import json
# 建立web伺服器的類
class HttpWebServer:
"""初始化套接字物件"""
def __init__(self, port):
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
tcp_server_socket.bind(('', port))
tcp_server_socket.listen(128)
self.tcp_server_socket = tcp_server_socket
@staticmethod
def get_response(msg):
# 呼叫圖靈機器人API
api = 'http://openapi.tuling123.com/openapi/api/v2'
data = { "perception": { "inputText": { "text": msg }, "inputImage": { "url": "imageUrl" }, "selfInfo": { "location": { "city": "成都", "province": "四川", "street": "" } } }, "userInfo": { "apiKey": '',# 填入申請的key "userId": "" } }
data = json.dumps(data) # 將字典格式轉化為json格式,另外loads函式是將json轉化為python中的字典
print(data)
# print('=================================================================================')
r = requests.post(api, data=data).json() # 將post請求的結果轉為json
print(r)
return r['results'][0]['values']['text'] # 返回的資料
@staticmethod
def client(new_socket):
"""新套接-請求-響應"""
# 接受客戶端訊息
while True:
recv_data = (new_socket.recv(4096))
recv_decode = recv_data.decode('utf-8')
# 判斷請求內容長度,若為0,則瀏覽器斷開連線
if len(recv_data) == 0:
print('offline')
new_socket.close()
return
print('帥哥:' + recv_decode)
response = HttpWebServer.get_response(recv_decode)
new_socket.send(response.encode('utf-8'))
def start(self):
"""開啟伺服器的方法"""
while True:
# 迴圈接受請求,並建立相應的套接字
new_socket, ip_port = self.tcp_server_socket.accept()
# 運用多執行緒實現多個客戶端訪問,並設定主執行緒守護
sub_threading = threading.Thread(target=self.client, args=(new_socket,), daemon=True)
# 子執行緒開啟
sub_threading.start()
def main():
"""程式入口"""
web_server = HttpWebServer(8989)
web_server.start()
if __name__ == '__main__':
main()複製程式碼
上面採用了threading實現了可多使用者聊天,並使用了守護主執行緒,防止了在主執行緒接受資料阻塞引起伺服器崩潰的情況。