python socket 实现点对点聊天,包括文字,语音,无声视频
client
#!/usr/bin/env python # -*- encoding: utf-8 -*- ''' @File : newclient.py @Time : 2020/01/13 14:17:16 @Author : Pan Rongfei @Version : 1.0 @Contact : bit_panrongfei@163.com 1838863836@gmail.com ''' # here put the import lib import threading import time import tkinter as tk import tkinter.messagebox import tkinter.scrolledtext as tst import wave from socket import * import os import cv2 import numpy as np import pyaudio class inputportdialog(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) self.master = master # 文本框中输入 self.portinput = tk.Text(self, width=30, height=5) self.portinput.grid(row=0, column=0, columnspan=1) self.ipinput = tk.Text(self, width=30, height=5) self.ipinput.grid(row=1, column=0, columnspan=1) tk.Button(self, text='确定', command=self.setIP).grid(row=1, column=2) self.grid() def setIP(self): global serverport global serverip # 获取文本内容 serverport = self.portinput.get('1.0', 'end-1c') serverport = int(serverport) serverip = self.ipinput.get('1.0', 'end-1c') serverip = int(serverip) # 销毁窗口 self.master.destroy() class Application(tk.Frame): def __init__(self, master): tk.Frame.__init__(self, master) self.grid() self.createWidgets() def createWidgets(self): # ----------------定义语音流--------------------- self.send_message = '' self.receive_message = '' self.audio = pyaudio.PyAudio() self.audio1 = pyaudio.PyAudio() self.audio_stream = self.audio.open(format=pyaudio.paInt16, channels=2, rate=44100, input=True, frames_per_buffer=1024) self.stream = self.audio1.open(format=pyaudio.paInt16, channels=2, rate=44100, output=True) self.frames = [] # 显示聊天窗口 self.clienttext = tst.ScrolledText(self, width=50, height=15) self.clienttext.grid(row=0, column=0, rowspan=1, columnspan=4) self.clienttext.config(state='disabled') # 定义标签,改变字体颜色 self.clienttext.tag_config('server', foreground='red') self.clienttext.tag_config('guest', foreground='blue') # 编辑输入框 self.inputText = tk.Text(self, width=40, height=7) self.inputText.grid(row=1, column=0, columnspan=1) # 定义快捷键,按下回车即可发送消息 self.inputText.bind("<KeyPress-Return>", self.textSendReturn) # 发送按钮,所有按钮包装在Frame容器内 frames = tk.Frame(self) frames.grid(row=1, column=3) # 文字发送 self.btnSend = tk.Button(frames, text='send', command=self.textSend) self.btnSend.pack(fill='x') # 语音录制及发送 self.recordstart = tk.Button(frames, text='record', command=self.wav_start) self.recordstart.pack(fill='x') self.recordend = tk.Button(frames, text='sendaudio', command=self.wavend) self.recordend.pack(fill='x') # 视频通信 self.vedio_button = tk.Button(frames, text='video', command=self.video) self.vedio_button.pack(fill='x') # 语音播放按钮 self.audio_button = tk.Button(self.clienttext, text='display', command=self.display_audio) # 退出 self.out = tk.Button(frames, text='quit', command=self.logout) self.out.pack(fill='x') # 开启线程 t = threading.Thread(target=self.getInfo) t.start() # 退出程序 def logout(self): os._exit(0) # 开始发送视频 def video(self): video_thread = threading.Thread(target=self.video_send) video_thread.start() # 视频发送 def video_send(self): cap = cv2.VideoCapture(0) encode_para = [int(cv2.IMWRITE_JPEG_QUALITY), 15] # 压缩参数 while cap.isOpened(): try: ret, frame = cap.read() time.sleep(0.1) # 时延保证稳定性 result, imgencode = cv2.imencode('.jpg', frame, encode_para) data = np.array(imgencode) string_data = np.ndarray.tostring(data) video_header = 'video' + '0' * (15 - len(str( len(string_data)))) + str(len(string_data)) + '###' video_header = bytes(video_header, encoding='utf8') video_message = video_header + string_data clientSocket.sendall(video_message) except: break cap.release() cv2.destroyAllWindows() # 开启语音录制 def wav_start(self): thread = threading.Thread(target=self.wavstart) thread.start() # 录制语音 def wavstart(self): # 循环获取语音 self.audio = pyaudio.PyAudio() self.audio_stream = self.audio.open(format=pyaudio.paInt16, channels=2, rate=44100, input=True, frames_per_buffer=1024) self.frames = [] while True: try: if self.audio_stream.is_stopped() is True: break data = self.audio_stream.read(1024) self.frames.append(data) except: break # 录制结束,发送语音 def wavend(self): # --------关闭语音获取并发送协议和语音流--------------- self.audio_stream.stop_stream() self.audio_stream.close() self.audio.terminate() # ---------音频发送----------------------------------- self.frames = b''.join(self.frames) self.send_message = bytes( ('audio' + '0' * (15 - len(str(len(self.frames)))) + str(len(self.frames)) + '###'), encoding='utf8') + self.frames clientSocket.sendall(self.send_message) def display_audio(self): # ----------------音频播放------------------------ data = self.wf.readframes(1024) while data != b'': self.stream.write(data) data = self.wf.readframes(1024) self.stream.close() self.audio1.terminate() # 发送文字 def textSend(self): string = self.inputText.get('1.0', 'end-1c') if string != "" and string is not None: # 显示发送时间和发送消息 timemsg = '客户端' + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' ' # -----------------------用户界面内容更新------------ # 通过设置state属性设置clienttext可编辑 self.clienttext.config(state='normal') self.clienttext.insert(tk.INSERT, ' ' + timemsg, 'guest') self.clienttext.insert(tk.INSERT, string + ' ') # 将滚动条拉到最后显示最新消息 self.clienttext.see(tk.END) # 通过设置state属性设置clienttext不可编辑 self.clienttext.config(state='disabled') self.inputText.delete(0.0, tk.END) # ---------------信息发送-------------------------- self.send_message = 'word' + '0' * ( 16 - len(str(len(string)))) + str(len(string)) + '###' + string clientSocket.send(bytes(self.send_message, encoding='utf8')) # ------------------------------------------------ else: tk.messagebox.showinfo('警告', "不能发送空白信息!") def getInfo(self): global clientSocket while True: recMessage = clientSocket.recv(1024) # total_data 中为数据内容 total_data = recMessage[23:] try: # 将header提取用作后续处理 self.receive_message = recMessage[0:23].decode() except: continue # --------------接收数据处理----------------------------- if self.receive_message == '': break recTime = '服务端' + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + ' ' # 通过header判断数据种类 # 文字 if self.receive_message.find('word') != -1: self.clienttext.config(state='normal') # server作为标签,改变字体颜色 self.clienttext.insert(tk.END, recTime, 'server') self.clienttext.insert(tk.END, total_data.decode()) # 将滚动条拉到最后显示最新消息 self.clienttext.see(tk.END) self.clienttext.config(state='disabled') # 语音 elif self.receive_message.find('audio') != -1: self.clienttext.config(state='normal') self.clienttext.insert(tk.END, ' ' + recTime, 'server') audio_length = self.receive_message.split('###')[0] audio_length = audio_length[5:20] audio_length = int(audio_length) self.receive_message = total_data list_frames = [] list_frames.append(self.receive_message) recv_length = 1024 while recv_length < audio_length: new_message = clientSocket.recv(9999) recv_length += len(new_message) list_frames.append(new_message) self.wf = wave.open('output.wav', 'wb') self.wf.setnchannels(2) self.wf.setsampwidth( self.audio.get_sample_size(pyaudio.paInt16)) self.wf.setframerate(44100) self.wf.writeframes(b''.join(list_frames)) self.wf.close() self.wf = wave.open('output.wav', 'rb') self.audio1 = pyaudio.PyAudio() self.stream = self.audio1.open( format=self.audio1.get_format_from_width( self.wf.getsampwidth()), channels=2, rate=self.wf.getframerate(), frames_per_buffer=1024, output=True) self.clienttext.window_create(tk.INSERT, window=self.audio_button) self.clienttext.see(tk.END) # 视频 elif self.receive_message.find('video') != -1: video_length = self.receive_message.split('###')[0] video_length = video_length[5:20] video_length = int(video_length) recv_video_length = 1024 video_frame = total_data while recv_video_length < video_length: new_video_data = clientSocket.recv(8000) recv_video_length += len(new_video_data) video_frame += new_video_data dataaa = np.frombuffer(video_frame, np.uint8) # 将获取到的字符流数据转换成1维数组 decimg = cv2.imdecode(dataaa, cv2.IMREAD_COLOR) # 将数组解码成图像 cv2.imshow('SERVER', decimg) if cv2.waitKey(1) & 0xFF == ord('q'): break # 将文字发送按键与回车键绑定 def textSendReturn(self, event): if event.keysym == "Return": self.textSend() # 指定服务器地址,端口 servername = '101.201.69.224' # 服务器ip,101.201.69.224为项目期间使用IP serverport = None # 端口 get_port = tk.Tk() get_port.title('输入目标端口及IP') portdialog = inputportdialog(get_port) portdialog.mainloop() clientSocket = socket(AF_INET, SOCK_STREAM) clientSocket.connect((servername, serverport)) root = tk.Tk() root.title('客户端') app = Application(master=root) app.mainloop()
Server
# !/usr/bin/env python # -*- conding:utf8 -*- import socket import select # 创建socket文件句柄 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 999999999) server.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 999999999) server.bind(('0.0.0.0', 8800)) server.listen(10) server.setblocking(0) epoll = select.epoll() epoll.register(server.fileno(), select.EPOLLIN) server2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server2.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 999999999) server2.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 999999999) server2.bind(('0.0.0.0', 8700)) server2.listen(10) server2.setblocking(0) epoll.register(server2.fileno(), select.EPOLLIN) server3 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server3.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server3.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 999999999) server3.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 999999999) server3.bind(('0.0.0.0', 8600)) server3.listen(10) server3.setblocking(0) epoll.register(server3.fileno(), select.EPOLLIN) server4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server4.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 999999999) server4.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 999999999) server4.bind(('0.0.0.0', 8500)) server4.listen(10) server4.setblocking(0) epoll.register(server4.fileno(), select.EPOLLIN) connections = {} requests = {} responses = {} while True: events = epoll.poll() for fileno, event in events: if fileno == server.fileno(): connection, addr = server.accept() connFd = connection.fileno() connection.setblocking(0) epoll.register(connFd, select.EPOLLIN) connections[connFd] = connection print('1') elif fileno == server2.fileno(): connection, addr = server2.accept() connFd = connection.fileno() connection.setblocking(0) epoll.register(connFd, select.EPOLLIN) connections[connFd] = connection print('2') elif fileno == server3.fileno(): connection, addr = server3.accept() connFd = connection.fileno() connection.setblocking(0) epoll.register(connFd, select.EPOLLIN) connections[connFd] = connection print('3') elif fileno == server4.fileno(): connection, addr = server4.accept() connFd = connection.fileno() connection.setblocking(0) epoll.register(connFd, select.EPOLLIN) connections[connFd] = connection print('4') elif event & select.EPOLLHUP: print('close') epoll.unregister(fileno) connections[fileno].close() del connections[fileno] elif event & select.EPOLLIN: requests[fileno] = connections[fileno].recv(999999) epoll.modify(fileno, select.EPOLLOUT) elif event & select.EPOLLOUT: print(connections) for i in connections: if i != fileno: connections[i].sendall(requests[fileno]) epoll.modify(fileno, select.EPOLLIN)