Python实现简易局域网视频聊天工具

ICanDo_It 2016-11-15 18:27:25 6571 收藏 8
分类专栏: Linux基础 文章标签: 算法
版权

     基于实验楼限免课程:

Python实现简易局域网视频聊天工具

     试验操作系统为Ubuntu14.04,OpenCV版本为2.4.13.1,源代码可以在https://github.com/Forec/lan-ichat上获取。软件初衷是为了在IPV6协议网络中节省聊天流量。内容简介:1.实现简易视频通信工具,并能实现语音通话 2.通信质量 ,如画质、停顿等参数可以调节  3.支持IPV6知识点:1.Python基于Opencv对摄像头信息的捕获和压缩2.Python关于线程和socket通信的基础技巧(数据的压缩处理为重点)3.Python基于Pyaudio对语音信息的捕获和压缩

期间遇到的问题:

     1.执行wgethttps://labfile.oss.aliyuncs.com/courses/672/2.4.13-binary.tar.gz操作时,遇到证书问题2.进入到python后回不去了(小白笨笨哒),http://blog.csdn.net/langzi7758521/article/details/51163009在这里找到了解法

即quit()/exit()/ctrl+d退出。。之前一直尝试cd …/cd ~无果

     3.import cv2时出现问题:failed to initialize libdc1394,百度后http://stackoverflow.com/questions/12689304/ctypes-error-libdc1394-error-failed-to-initialize-libdc1394中找到解法。4.最后仍是无法查看CV2的版本,原因是无法打出更长的下划线。。无语无奈

无法下载链接中的文件,于是通过实验楼中的火狐浏览器直接下载后,复制到Opencv文件夹下,进行下一步操作成功。

过程理解:

     1.更新实验楼中一些基本库2.新建OpenCV文件夹,下载作者已编译好的包,完成环境配置的准备工作

Python实现简易局域网视频聊天工具

一、课程介绍

1.课程来源

课程使用的操作系统为 Ubuntu 14.04,OpenCV版本为OpenCV 2.4.13.1,你可以在这里查看该版本 OpenCV 的文档。

你可以在我的 Github 上找到 Windows 系统和 Linux 系统对应的源代码,此教程对应的版本是 v0.2。目前我正在开发的版本是 v0.3,新版本将允许使用不同IP协议的主机通信,并且范围不再局限于局域网内。这个工具最初是为了通过IPv6节省聊天工具使用的流量而开发的。

2.内容简介

课程实验实现简易的视频通信工具
在视频通信的基础上加入语音
用户可以选择通信的质量,即画质、停顿等参数
支持IPv6

3.课程知识点

本课程项目完成过程中将学习:

Python 基于 OpenCV 对摄像头信息的捕获和压缩
Python 关于 线程 和 socket 通信的一些基础技巧
Python 基于 PyAudio 对语音信息的捕获和压缩

其中将重点介绍 socket 传输过程中对数据的压缩和处理。

二、实验环境

本实验需要先在实验平台安装 OpenCV ,需下载依赖的库、源代码并编译安装。安装过程建议按照教程给出的步骤,或者你可以参考官方文档中 Linux 环境下的安装步骤,但 有些选项需要变更。安装过程所需时间会比较长,这期间你可以先阅读接下来的教程,在大致了解代码原理后再亲自编写尝试。
我提供了一个编译好的2.4.13-binary.tar.gz包,你可以通过下面的命令下载并安装,节省了编译的时间,但仍需要安装依赖库的过程,整个环境配置过程大约20~30分钟,根据当前机器速度决定。

· $ sudo apt-get update

· $ sudo apt-get install build-essentiallibgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev cmakepython-dev python-numpy python-tk libtbb-dev libeigen2-dev yasm libfaac-devlibopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-devlibxvidcore-dev libx264-dev libqt4-dev libqt4-opengl-dev sphinx-commontexlive-latex-extra libv4l-dev libdc1394-22-dev libavcodec-dev libavformat-devlibswscale-dev python-pyaudio

· $ cd ~

· $ mkdir OpenCV && cd OpenCV

· $ wgethttps://labfile.oss.aliyuncs.com/courses/672/2.4.13-binary.tar.gz

· $ tar -zxvf 2.4.13-binary.tar.gz

· $ cdopencv-2.4.13

· $ cd build

· $ sudo make install

如果你想体验编译的整个过程,我也提供了一个一键安装的脚本文件,你可以通过下面的命令尝试。这个过程会非常漫长,期间可能还需要你做一定的交互确认工作。

· $ cd ~

· $ sudo apt-get update

· $ wgethttps://labfile.oss.aliyuncs.com/courses/672/opencv.sh

· $ sudo chmod 777 opencv.sh

· $ ./opencv.sh

如果你觉得有必要亲自尝试一下安装的每一步,可以按照下面的命令逐条输入执行,在实验楼的环境中大概需要两个小时。

· $ sudo apt-get update

· $ sudo apt-get install build-essentiallibgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev cmakepython-dev python-numpy python-tk libtbb-dev libeigen2-dev yasm libfaac-devlibopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-devlibx264-dev libqt4-dev libqt4-opengl-dev sphinx-common texlive-latex-extralibv4l-dev libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-devpython-pyaudio

· $ wgethttps://github.com/Itseez/opencv/archive/2.4.13.zip

· $ unzip 2.4.13.zip

· $ cd 2.4.13

· $ mkdir release && cd release

· $ cmake -D WITH_TBB=ON -DBUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D INSTALL_C_EXAMPLES=ON -DINSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON -D WITH_QT=ON -D WITH_GTK=ON -DWITH_OPENGL=ON …

· $ sudo make

· $ sudo make install

· $ sudo gedit/etc/ld.so.conf.d/opencv.conf

· $ 输入 /usr/local/lib,按 Ctrl + X 退出,退出时询问是否保存,按 Y 确认。

· $ sudo ldconfig -v

· $ sudo gedit /etc/bash.bashrc

· $ 在文件末尾加入

· $ PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig

· export PKG_CONFIG_PATH

· 按 Ctrl + X 退出,按 Y 确认保存。

检验配置是否成功。

· $ python

· >>> import cv2

· >>> cv2.version

· ‘2.4.13’

三、实验原理

实验实现了简易的视频通信工具,基于 OpenCV 和 PyAudio,使用 TCP 协议通信,通信双方建立双向 CS 连接,双方均维护一个客户端和一个服务器端。在捕获视频信息后,根据用户指定的参数对画面做压缩并传输。

四、实验步骤

通过以下命令可下载项目源码,作为参照对比完成下面详细步骤的学习。

wgethttps://labfile.oss.aliyuncs.com/courses/672/ichat.zip

unzip ichat.zip

1.实现双向 C/S 连接

先为双方的通信设计 Server 类和 Client类,两个类均继承threading.Thread,只需要分别实现 init、__del__和run方法,之后对象调用.start()方法即可在独立线程中执行run方法中的内容。首先Client类需要存储远端的IP地址和端口,而Server类需要存储本地服务器监听的端口号。用户还应当可以指定通信双方使用的协议版本,即基于IPv4 还是IPv6 的TCP连接。因此Server类的初始化需要传入两个参数(端口、版本),Client类的初始化需要三个参数(远端IP、端口、版本)。新建文件vchat.py,在其中定义基础的两个类如下。

from socket import *

import threading

classVideo_Server(threading.Thread):

def__init__(self, port, version) :

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = ('', port)if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)

def__del__(self):

    self.sock.close()# TODO

defrun(self):

    print("server starts...")self.sock.bind(self.ADDR)self.sock.listen(1)conn, addr = self.sock.accept()print("remote client success connected...")# TODO

classVideo_Client(threading.Thread):

def__init__(self ,ip, port, version):

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = (ip, port)if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)

def__del__(self) :

    self.sock.close()# TODO

defrun(self):

    print("client starts...")whileTrue:try:self.sock.connect(self.ADDR)breakexcept:time.sleep(3)continueprint("client connected...")# TODO

2.实现摄像头数据流捕获

OpenCV 为Python 提供的接口非常简单并且易于理解。捕获视频流的任务应当由Client类完成,下面完善Client的run函数。在下面的代码中,我们为类添加了一个成员变量cap,它用来捕获默认摄像头的输出。

classVideo_Client(threading.Thread):

def__init__(self ,ip, port, version):

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = (ip, port)if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)self.cap = cv2.VideoCapture(0)

def__del__(self) :

    self.sock.close()self.cap.release()

defrun(self):

    print("client starts...")whileTrue:try:self.sock.connect(self.ADDR)breakexcept:time.sleep(3)continueprint("client connected...")whileself.cap.isOpened():ret, frame = self.cap.read()# TODO

3.发送捕获到的数据到服务器

已经捕获到数据,接下来要发送字节流。首先我们继续编写Client,为其添加发送数据功能的实现。这里只改动了run方法。在捕获到帧后,我们使用pickle.dumps方法对其打包,并用sock.sendall方法发送。注意发送过程中我们用struct.pack方法为每批数据加了一个头,用于接收方确认接受数据的长度。

defrun(self):

    whileTrue:try:self.sock.connect(self.ADDR)breakexcept:time.sleep(3)continueprint("client connected...")whileself.cap.isOpened():ret, frame = self.cap.read()data = pickle.dumps(frame)try:self.sock.sendall(struct.pack("L", len(data))+ data)except:break

下面编写Server,在服务器端连接成功后,应当创建一个窗口用于显示接收到的视频。因为连接不一定创建成功,因此cv.destroyAllWindows()被放在一个try…catch块中防止出现错误。在接收数据过程中,我们使用payload_size记录当前从缓冲区读入的数据长度,这个长度通过struct.calcsize(‘L’)来读取。使用该变量的意义在于缓冲区中读出的数据可能不足一个帧,也可能由多个帧构成。为了准确提取每一帧,我们用payload_size区分帧的边界。在从缓冲区读出的数据流长度超过payload_size时,剩余部分和下一次读出的数据流合并,不足payload_size时将合并下一次读取的数据流到当前帧中。在接收完完整的一帧后,显示在创建的窗口中。同时我们为窗口创建一个键盘响应,当按下Esc 或 q键时退出程序。

classVideo_Server(threading.Thread):

def__init__(self, port, version) :

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = ('', port)if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)

def__del__(self):

    self.sock.close()try:cv2.destroyAllWindows()except:pass

defrun(self):

    print("server starts...")self.sock.bind(self.ADDR)self.sock.listen(1)conn, addr = self.sock.accept()print("remote client successconnected...")data = "".encode("utf-8")payload_size = struct.calcsize("L")cv2.namedWindow('Remote', cv2.WINDOW_NORMAL)whileTrue:while len(data)< payload_size:data += conn.recv(81920)packed_size = data[:payload_size]data = data[payload_size:]msg_size = struct.unpack("L", packed_size)[0]while len(data)< msg_size:data += conn.recv(81920)zframe_data = data[:msg_size]data = data[msg_size:]frame_data =zlib.decompress(zframe_data)frame = pickle.loads(frame_data)cv2.imshow('Remote', frame)if cv2.waitKey(1) & 0xFF == 27:break

4.视频缩放和数据压缩

现在的服务器和客户端已经可以运行,你可以在代码中创建一个Client类实例和一个Server类实例,并将IP地址设为127.0.0.1,端口设为任意合法的(0-65535)且不冲突的值,版本设为IPv4。执行代码等同于自己和自己通信。如果网络状况不好,你也许会发现自己和自己的通信也有卡顿现象。为了使画面质量、延迟能够和现实网络状况相匹配,我们需要允许用户指定通信中画面的质量,同时我们的代码应当本身具有压缩数据的能力,以尽可能利用带宽。

当用户指定使用低画质通信,我们应当对原始数据做变换,最简单的方式即将捕获的每一帧按比例缩放,同时降低传输的帧速,在代码中体现为resize,该函数的第二个参数为缩放中心,后两个参数为缩放比例,并且根据用户指定的等级,不再传输捕获的每一帧,而是间隔几帧传输一帧。为了防止用户指定的画质过差,代码中限制了最坏情况下的缩放比例为0.3,最大帧间隔为3。此外,我们在发送每一帧的数据前使用zlib.compress对其压缩,尽量降低带宽负担。

classVideo_Client(threading.Thread):

def__init__(self ,ip, port, level, version):

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = (ip, port)if level <= 3:self.interval = levelelse:self.interval = 3self.fx = 1 /(self.interval + 1)if self.fx <0.3:self.fx = 0.3if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)self.cap = cv2.VideoCapture(0)

def__del__(self) :

    self.sock.close()self.cap.release()

defrun(self):

    print("VEDIO client starts...")whileTrue:try:self.sock.connect(self.ADDR)breakexcept:time.sleep(3)continueprint("VEDIO client connected...")whileself.cap.isOpened():ret, frame = self.cap.read()sframe = cv2.resize(frame, (0,0),fx=self.fx, fy=self.fx)data = pickle.dumps(sframe)zdata = zlib.compress(data,zlib.Z_BEST_COMPRESSION)try:self.sock.sendall(struct.pack("L", len(zdata))+ zdata)except:breakfor i inrange(self.interval):self.cap.read()

服务器端最终代码如下,增加了对接收到数据的解压缩处理。

classVideo_Server(threading.Thread):

def__init__(self, port, version) :

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = ('', port)if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)

def__del__(self):

    self.sock.close()try:cv2.destroyAllWindows()except:pass

defrun(self):

    print("VEDIO server starts...")self.sock.bind(self.ADDR)self.sock.listen(1)conn, addr = self.sock.accept()print("remote VEDIO client successconnected...")data = "".encode("utf-8")payload_size = struct.calcsize("L")cv2.namedWindow('Remote',cv2.WINDOW_NORMAL)whileTrue:while len(data)< payload_size:data += conn.recv(81920)packed_size = data[:payload_size]data = data[payload_size:]msg_size = struct.unpack("L",packed_size)[0]while len(data)< msg_size:data += conn.recv(81920)zframe_data = data[:msg_size]data = data[msg_size:]frame_data =zlib.decompress(zframe_data)frame = pickle.loads(frame_data)cv2.imshow('Remote', frame)if cv2.waitKey(1) & 0xFF == 27:break

5.加入音频的捕获和传输

在完成视频通信的基础上,整体框架对于音频通信可以直接挪用,只需要修改其中捕获视频/音频的代码和服务器解码播放的部分。这里我们使用 PyAudio 库处理音频,在 Linux 下你也可以选择 sounddevice。关于sounddevice这里不做过多介绍,你可以在这里看到它最新版本的文档。将vchat.py复制一份,重命名为achat.py,简单修改几处,最终音频捕获、传输的完整代码如下。我将上面代码中的Server和Client分别加上Video和Audio前缀以区分,同时显示给用户的print输出语句也做了一定修改,对于视频加上VIDEO前缀,音频加上AUDIO前缀。如果你对代码中使用到的 PyAudio 提供的库函数有所疑问,可以在这里找到相关的入门文档及示例。

classAudio_Server(threading.Thread):

def__init__(self, port, version) :

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = ('', port)if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)self.p = pyaudio.PyAudio()self.stream = None

def__del__(self):

    self.sock.close()if self.stream isnotNone:self.stream.stop_stream()self.stream.close()self.p.terminate()

defrun(self):

    print("AUDIO server starts...")self.sock.bind(self.ADDR)self.sock.listen(1)conn, addr = self.sock.accept()print("remote AUDIO client success connected...")data = "".encode("utf-8")payload_size = struct.calcsize("L")self.stream =self.p.open(format=FORMAT,channels=CHANNELS,rate=RATE,output=True,frames_per_buffer = CHUNK)whileTrue:while len(data)< payload_size:data += conn.recv(81920)packed_size = data[:payload_size]data = data[payload_size:]msg_size = struct.unpack("L",packed_size)[0]while len(data)< msg_size:data += conn.recv(81920)frame_data = data[:msg_size]data = data[msg_size:]frames = pickle.loads(frame_data)for frame in frames:self.stream.write(frame, CHUNK)

classAudio_Client(threading.Thread):

def__init__(self ,ip, port, version):

    threading.Thread.__init__(self)self.setDaemon(True)self.ADDR = (ip, port)if version == 4:self.sock = socket(AF_INET,SOCK_STREAM)else:self.sock = socket(AF_INET6,SOCK_STREAM)self.p = pyaudio.PyAudio()self.stream = None

def__del__(self) :

    self.sock.close()if self.stream isnotNone:self.stream.stop_stream()self.stream.close()self.p.terminate()

defrun(self):

   print("AUDIO client starts...")whileTrue:try:self.sock.connect(self.ADDR)breakexcept:time.sleep(3)continueprint("AUDIO client connected...")self.stream =self.p.open(format=FORMAT,channels=CHANNELS,rate=RATE,input=True,frames_per_buffer=CHUNK)whileself.stream.is_active():frames = []for i in range(0, int(RATE /CHUNK * RECORD_SECONDS)):data = self.stream.read(CHUNK)frames.append(data)senddata = pickle.dumps(frames)try:self.sock.sendall(struct.pack("L",len(senddata)) + senddata)except:break

6.编写程序入口 main.py

为了提供用户参数解析,代码使用了argparse。你可能对此前几个类中初始化方法的self.setDaemon(True)有疑惑。这个方法的调用使每个线程在主线程结束之后自动退出,保证程序不会出现崩溃且无法销毁的情况。在main.py中,我们通过每隔1s做一次线程的保活检查,如果视频/音频中出现阻塞/故障,主线程会终止。

import sys

import time

import argparse

from vchat importVideo_Server, Video_Client

from achat importAudio_Server, Audio_Client

parser = argparse.ArgumentParser()

parser.add_argument(’–host’, type=str,default=‘127.0.0.1’)

parser.add_argument(’–port’, type=int,default=10087)

parser.add_argument(’–level’, type=int,default=1)

parser.add_argument(’-v’, ‘–version’, type=int,default=4)

args = parser.parse_args()

IP = args.host

PORT = args.port

VERSION = args.version

LEVEL = args.level

if name == ‘main’:

vclient = Video_Client(IP, PORT, LEVEL, VERSION)

vserver = Video_Server(PORT, VERSION)

aclient = Audio_Client(IP, PORT+1, VERSION)

aserver = Audio_Server(PORT+1, VERSION)

vclient.start()

aclient.start()

time.sleep(1) # make delay to start server

vserver.start()

aserver.start()

whileTrue:

    time.sleep(1)ifnotvserver.isAlive() ornotvclient.isAlive():print("Video connection lost...")sys.exit(0)ifnotaserver.isAlive() ornotaclient.isAlive():print("Audio connection lost...")sys.exit(0)

7.运行情况

因为实验楼的环境没有提供摄像头,因此我们需要修改一下代码,让程序从一个本地视频文件读取,模拟摄像头的访问。将Video_Client中self.cap =cv2.VideoCapture(0)改为self.cap =cv2.VideoCapture(‘test.mp4’),即从本地视频test.mp4中读取。在修改完你的代码后,你可以通过以下命令下载test.mp4(该视频文件是周杰伦《浪漫手机》的MV),并检验代码。

$ wgethttp://labfile.oss.aliyuncs.com/courses/671/test.mp4

$ python2 main.py

和上面命令一样,在本机可以通过 python2 main.py 来实验本机和本机的视频聊天,如果你有条件在同一局域网内的两台机器上实验,则可以将程序部署在两台机器上,并相互连接观察效果。下面两张图为本机上实验截图,有些情况下 PyAudio 可能会提示一些警告,你可以忽视它的提示。用户也可以指定level参数,level越高,画质越差,level为0 为原始画面,在我们的main.py中默认level为1。

通过在某高校校园网内验证,程序可以保证长时间顺畅通话,偶尔会出现网络质量较差导致的短暂卡顿,不影响实际视频通话效果。

五、代码获取

你可以在我的 Github仓库 中获取到完整的代码,里面提供了Windows 版本和 Linux版本的配置、运行方案。如果你有建议或想法,欢迎提 PR 沟通。

Python实现简易局域网视频聊天工具相关推荐

  1. python语言视频-Python语言之Python3 实现简易局域网视频聊天工具

    本文主要向大家介绍了Python语言之Python3 实现简易局域网视频聊天工具,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. 操作系统为 Ubuntu 16.04,OpenCV ...

  2. Python3 实现简易局域网视频聊天工具

    Python3 实现简易局域网视频聊天工具 1.环境 操作系统为 Ubuntu 16.04 python 3.5 opencv-python 3.4.1.15 numpy 1.14.5 PyAudio ...

  3. 企业如何远程招聘到靠谱的程序员?--Codassium网页视频聊天工具

    招聘合适的人才一直是很多企业的痛,而要招聘到靠谱的程序员更不是那么容易的一件事,特别是对于创业公司来说更是如此.你需要了解对方的气质是否契合公司的文化氛围,还要了解他有没有真实的编程能力. 如果对方没 ...

  4. 局域网即时聊天工具都有哪些?

    在互联网普及的今天,使用即时聊天工具沟通交流在人们的日常生活和工作当中已经十分普及.但由于企业内部敏感信息通过互联网泄露的现象频发,不少企业出于安全性考虑只能转用局域网环境办公,在转用局域网环境办公后 ...

  5. 使用Python实现简易的数据标注工具

    使用Python实现简易的数据标注工具 以增加工作效率为目的,最近一直在着手构建一个AI ToolBox 这两天,我为其中的预处理工具目录添加了数据标注模块,本文所介绍内容的代码见这里 该数据标注模块 ...

  6. 基于UDP协议的局域网网络聊天工具

    /* * 本程序实现了基于UDP协议的局域网网络聊天工具. * 参考网上的源码,发现一个calss就可以搞定. * ChatFrame类创建窗口,包含JTextField和TextArea. * 前者 ...

  7. 5个人审查5开源视频聊天工具

    像世界上大多数其他国家一样,卡在室内的是一群Opensource.com编辑和通讯员- 塞斯·肯隆 ( Seth Kenlon) , 马特 ·布罗伯格 ( Matt Broberg) , 艾伦·福迪· ...

  8. 简易局域网视频服务器

    1 出发点及效果图 手机存储空间太小,放不下几十级的视频,网络看又太卡.这个小项目帮助大家在局域网中建立一个像youku一样的视频服务器,这样就可以在家里通过局域网来观看服务 器(计算机)上的视频了. ...

  9. 内网python 仓库_GitHub - xanarry/LanTrans-desktop: 这是一个用python写的局域网传文件工具, 跨平台, 仓库中还有安卓版的工程...

    文档(代码比较乱) ubuntu效果 windows效果 主程序, Lantans_desktop.py 运行时所需环境: python3.4, PyQT5 如果接收过程无法被扫描到, 请关闭防火墙 ...

  10. 使用UDP协议构建简易局域网内聊天室

    实现功能: 客户端 1.开启时输入昵称并往服务器端发送带有客户端昵称 2.开启后能随时往服务器端发送聊天内容 3.收到服务器发来的数据时判别是否为自己发送的数据,若不是则输出num编号所指引的内容 4 ...

最新文章

  1. Microbiome:根系分泌物驱动土壤记忆抵御植物病原菌(作者解读)
  2. 一个具有多模型融合能力的网络或许是这样的
  3. 给页面点击链接加了转圈圈和解决遇到的bug
  4. android获取app用户数据,Android 原生app获取用户授权访问Autodesk云应用数据
  5. Faster\Slower 快慢指针的应用
  6. [Qt入门]QMainWindow创建
  7. oracle修改某表中的顺序,oracle 数据库 , 表中字段顺序修改
  8. 2020亚太内容分发大会 阿里云荣获“边缘计算领航企业”奖
  9. 发送邮件 ajax,使用Ajax发送电子邮件
  10. docker build 变量_Docker从入门到掉坑(二):基于Docker构建SpringBoot微服务
  11. 01数据结构——绪论
  12. 永远不要在代码中使用“User”这个单词
  13. python selenium打开新窗口,python selenium click打开新窗口
  14. 计算机类课题研究方法,课题研究方法有哪些
  15. 清华大学计算机系学术委员会,清华大学学术委员会召开2018年度全体会议
  16. win10关闭自带的杀毒
  17. App Inventor学习环境搭建
  18. cgb2107-day07
  19. 通过京东商品ID获取京东优惠券信息,京东优惠券信息接口,京东优惠券API接口,接口说明接入方案
  20. 金融经济学(王江)期末梳理 第十三章 资本资产定价模型(CAPM)

热门文章

  1. 计算机数据类型误差怎么解决,测绘数据常见误差类型及处理方法
  2. access 数据库模糊查询
  3. 使用 Python 开发 QGIS 插件
  4. 推荐克莱夫·汤普森《天才程序员》
  5. OSAL启动过程分析
  6. Mac 怎样安装虚拟机(VMware fusion 12)
  7. ZZULIOJ1016
  8. 美通企业日报 | 爱彼迎成为奥林匹克全球合作伙伴;腾讯理财通运营主体迁至腾富公司...
  9. 兼容所有浏览器的快速Web打印控件下载
  10. Switch 硬件破解经验分享 - 术语篇