第1章套接字、IPv4和简单的客户端/服务器编程

本章攻略:

打印设备名和IPv4地址

获取远程设备的IP地址

将IPv4地址转换成不同的格式

通过指定的端口和协议找到服务名

主机字节序和网络字节序之间相互转换

设定并获取默认的套接字超时时间

优雅地处理套接字错误

修改套接字发送和接收的缓冲区大小

把套接字改成阻塞或非阻塞模式

重用套接字地址

从网络时间服务器上获取并打印当前时间

编写一个SNTP客户端

编写一个简单的回显客户端/服务器应用

1.1简介

本章通过一些简单的攻略介绍Python的核心网络库。Python的socket模块提供了类方法和实例方法,二者的区别在于使用类方法时不需要创建套接字对象实例。这是一种很直观的方法。例如,打印设备的IP地址不需要创建套接字对象,而只需调用套接字的类方法。但是,如果要把数据发送给服务器程序,那么创建一个套接字对象来处理具体的操作则更加自然。本章介绍的攻略可以分成如下三类:

前几个攻略使用类方法获取关于主机、网络以及目标服务的有用信息;

随后的几个攻略使用实例方法,演示了常用的套接字操作,例如处理套接字超时、缓冲区大小和阻塞模式等;

.  最后,结合使用类方法和实例方法开发客户端,执行一些实际的任务,例如使设备时间与网络服务器同步,编写通用的客户端/服务器脚本。

你可以使用本章演示的方法编写自己的客户端/服务器应用。

1.2打印设备名和IPv4地址

有时,你需要快速查看设备的某些信息,例如主机名、IP地址和网络接口的数量等。这些信息使用Python脚本很容易获取。

1.2.1准备工作

编写代码之前先要在设备上安装Python。大多数Linux发行版都预装了Python。如果使用微软Windows操作系统,可以从Python的网站上下载二进制文件:http://www.python.org/download/。

要了解系统是否已经安装了Python,可以查阅操作系统的文档。在设备上安装好Python之后,可以在命令行中输入python,尝试打开Python解释器。输入python后应该显示解释器提示符))),具体的输出如下所示:

~$ python

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24)

[GCC 4.5.2] on linux2

Type "help", "copyright", "credits" or "license" for more information. )))

1.2.2实战演练

这个攻略很简短,可以直接写在Python解释器中。

首先,使用下面的命令导入Python中的socket库:

))) import socket

然后,调用socket库提供的gethostname()方法,把结果保存在一个变量中,如下所示:

))) host_name = socket.gethostname()

))) print "Host name: %s" %host_name

Host name: debian6

))) print "IP address: %s" %socket.gethostbyname(host_name)

IP address: 127.0.1.1

这些操作可以使用内置的类方法,定义成一个独立的函数print_machine_info()。

我们要在常用的__main__代码块中调用这个函数。运行时,Python会为某些内部变量赋值, 例如__name__。在这里,__name__表示调用程序的进程名。如果在命令行中运行脚本(如后面的命令所示),__name__的值是__main__。但是,如果在其他脚本中导入,情况就不同了。也就是说,如果在命令行中调用这个模块,会自动运行print_machine_info()函数;如果在其他脚本中导入,用户就要手动调用这个函数。

代码清单1-1展示了如何获取设备的信息,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter -1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import socket

def print_machine_info():

host_name = socket.gethostname()

ip_address = socket.gethostbyname(host_name)

print "Host name: %s" % host_name

print "IP address: %s" % ip_address

if __name__ == '__main__':

print_machine_info()

若想运行这个脚本,要在命令行中指定源码文件,如下所示:

$ python 1_1_local_machine_info.py

在我的设备上,显示了如下输出:

Host name: debian6

IP address: 127.0.0.1

在你的设备上,输出的内容根据系统的主机配置会有所不同。

1.2.3原理分析

import socket语句导入Python提供的一个核心网络库。然后调用两个工具函数:gethostname()和gethostbyname(host_name)。在命令行中可以输入help(socket.geth- ostname)查看帮助信息,或者在浏览器中访问http://docs.python.org/3/library/socket.html。在命令行中查看这两个函数的帮助信息,得到的输出如下:

gethostname(...)

gethostname() -) string

Return the current host name.

gethostbyname(...)

gethostbyname(host) -) address

Return the IP address (a string of the form '255.255.255.255') for a host.

第一个函数没有参数,返回所在主机或本地主机的名字。第二个函数接收一个参数hostname,返回对应的IP地址。

1.3获取远程设备的IP地址

有时需要把设备的主机名转换成对应的IP地址,例如快速查询域名。本攻略介绍一个简单的函数来完成这一操作。

1.3.1实战演练

如果想知道远程设备的IP地址,可以使用内置的库函数gethostbyname(),其参数是远程设备的主机名。

这里,我们要调用的是类函数gethostbyname()。让我们来看一下这个简短的代码片段。

代码清单1-2展示了如何获取远程设备的IP地址,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

def get_remote_machine_info():

remote_host = 'www.python.org'

try:

print "IP address: %s" %socket.gethostbyname(remote_host)

except socket.error, err_msg:

print "%s: %s" %(remote_host, err_msg)

if __name__ == '__main__':

get_remote_machine_info()

运行上述代码会得到以下输出:

$ python 1_2_remote_machine_info.py

IP address of www.python.org: 82.94.164.162

1.3.2原理分析

这个攻略把gethostbyname()方法包装在用户定义的get_remote_machine_info()函数中,还引入了异常处理的概念。如上述代码所示,我们把主要的函数调用放在try-except块中,这就意味着,如果执行函数gethostbyname()的过程中发生了错误,这个错误将由try-except块处理。

假如我们修改remote_host参数的值,把www.python.org改成一个不存在的域名,例如www.pytgo.org,然后执行下述命令:

$ python 1_2_remote_machine_info.py

www.pytgo.org: [Errno -5] No address associated with hostname

try-except块捕获了错误,并向用户显示了一个错误消息,说明域名www.pytgo.org没有对应的IP地址。

1.4将IPv4地址转换成不同的格式

如果要使用低层网络函数,有时普通的字符串形式的IP地址并不是很有用,需要把它们转换成打包后的32位二进制格式。

1.4.1实战演练

Python的socket库提供了很多用来处理不同IP地址格式的函数,这里我们使用其中的两个:inet_aton()和inet_ntoa()。

我们来定义convert_ip4_address()函数,调用inet_aton()和inet_ntoa()转换IP地址。我们要使用两个示例IP地址:127.0.0.1和192.168.0.1。

代码清单1-3展示了如何定义convert_ip4_address()函数,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

from binascii import hexlify

def convert_ip4_address():

for ip_addr in ['127.0.0.1', '192.168.0.1']:

packed_ip_addr = socket.inet_aton(ip_addr)

unpacked_ip_addr = socket.inet_ntoa(packed_ip_addr)

print "IP Address: %s =) Packed: %s, Unpacked: %s"\

%(ip_addr, hexlify(packed_ip_addr), unpacked_ip_addr)

if __name__ == '__main__':

convert_ip4_address()

现在,运行这个攻略,会看到以下输出:

$ python 1_3_ip4_address_conversion.py

IP Address: 127.0.0.1 =) Packed: 7f000001, Unpacked: 127.0.0.1

IP Address: 192.168.0.1 =) Packed: c0a80001, Unpacked: 192.168.0.1

1.4.2原理分析

在这个攻略中,使用for-in语句把两个字符串形式的IP地址转换成打包后的32位二进制格式,而且还调用了binascii模块中的hexlify函数,以十六进制形式表示二进制数据。

1.5通过指定的端口和协议找到服务名

如果想找到网络服务,最好知道该服务运行在TCP或UDP协议的哪个端口上。

1.5.1准备工作

如果知道网络服务使用的端口,可以调用socket库中的getservbyport()函数来获取服务的名字。调用这个函数时可以根据情况决定是否提供协议名。

1.5.2实战演练

我们来定义find_service_name()函数,在Python的for-in循环中调用函数getservbyport(),解析几个端口,例如80和25。

代码清单1-4展示了如何定义find_service_name()函数,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

def find_service_name():

protocolname = 'tcp'

for port in [80, 25]:

print "Port: %s =) service name: %s" %(port, socket.getservbyport(port, protocolname))

print "Port: %s =) service name: %s" %(53, socket.getservbyport(53, 'udp'))

if __name__ == '__main__':

find_service_name()

运行这个脚本,会看到如下输出:

$ python 1_4_finding_service_name.py

Port: 80 =) service name: http

Port: 25 =) service name: smtp

Port: 53 =) service name: domain

1.5.3原理分析

在这个攻略中,使用for-in语句遍历一组变量。在每次遍历中,获取端口对应的服务名。

1.6主机字节序和网络字节序之间相互转换

编写低层网络应用时,或许需要处理通过电缆在两台设备之间传送的低层数据。在这种操作中,需要把主机操作系统发出的数据转换成网络格式,或者做逆向转换,因为这两种数据的表示方式不一样。

1.6.1实战演练

Python的socket库提供了将数据在网络字节序和主机字节序之间相互转换的函数。你可能想了解这些函数,例如ntohl()和htonl()。

我们来定义convert_integer()函数,调用ntohl()和htonl()类函数来转换不同格式的数据。

代码清单1-5展示了如何定义convert_integer()函数,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter -1

# This program is optimized for Python 2.7.

# It may run on any other version with/without modifications.

import socket

def convert_integer():

data = 1234

# 32-bit

print "Original: %s =) Long host byte order: %s, Network byte order: %s"\

%(data, socket.ntohl(data), socket.htonl(data))

# 16-bit

print "Original: %s =) Short host byte order: %s, Network byte order: %s"\

%(data, socket.ntohs(data), socket.htons(data))

if __name__ == '__main__':

convert_integer()

运行这个攻略,会看到以下输出:

$ python 1_5_integer_conversion.py

Original: 1234 =) Long host byte order: 3523477504, Network byte order: 3523477504

Original: 1234 =) Short host byte order: 53764, Network byte order: 53764

1.6.2原理分析

在这个攻略中,我们以整数为例,演示了如何把它转换成网络字节序和主机字节序。socket库中的类函数ntohl()把网络字节序转换成了长整形主机字节序。函数名中的n表示网络;h表示主机;l表示长整形;s表示短整形,即16位。

1.7设定并获取默认的套接字超时时间

有时,你需要处理socket库某些属性的默认值,例如套接字超时时间。

1.7.1实战演练

你可以创建一个套接字对象实例,调用gettimeout()方法获取默认的超时时间,调用settimeout()方法设定一个超时时间。这种操作在开发服务器应用时很有用。

在test_socket_timeout()函数中,首先创建一个套接字对象,然后使用读取或者设定实例方法处理超时时间。

代码清单1-6展示了如何定义test_socket_timeout()函数,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import socket

def test_socket_timeout():

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print "Default socket timeout: %s" %s.gettimeout()

s.settimeout(100)

print "Current socket timeout: %s" %s.gettimeout()

if __name__ == '__main__':

test_socket_timeout()

运行上述代码后,你会看到它是如何修改默认超时时间的,如下所示:

$ python 1_6_socket_timeout.py

Default socket timeout: None

Current socket timeout: 100.0

1.7.2原理分析

在这段代码片段中,首先创建了一个套接字对象。套接字构造方法的第一个参数是地址族,第二个参数是套接字类型。然后,调用gettimeout()方法获取套接字超时时间,再调用settimeout()方法修改超时时间。传给settimeout()方法的参数可以是秒数(非负浮点数)也可以是None。这个方法在处理阻塞式套接字操作时使用。如果把超时时间设为None,则禁用了套接字操作的超时检测。

1.8优雅地处理套接字错误

在网络应用中,经常会遇到这种情况:一方尝试连接,但另一方由于网络媒介失效或者其他原因无法响应。Python的socket库提供了一个方法,能通过socket.error异常优雅地处理套接字错误。在这个攻略中会举几个例子。

1.8.1实战演练

我们来编写几个try-except代码块,每个块对应一种可能发生的错误。为了获取用户输入,可以使用argparse模块。这个模块的功能很强大,而不仅是可以使用sys.argv解析命令行参数。这些try-except代码块分别演示了常见的套接字操作,例如创建套接字对象、连接服务器、发送数据和等待应答。

下述攻略使用几行代码演示了如何处理异常。

代码清单1-7展示了如何处理socket.error异常,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import sys

import socket

import argparse

def main():

# setup argument parsing

parser = argparse.ArgumentParser(description='Socket Error Examples')

parser.add_argument('--host', action="store", dest="host", required=False)

parser.add_argument('--port', action="store", dest="port", type=int, required=False)

parser.add_argument('--file', action="store", dest="file", required=False)

given_args = parser.parse_args()

host = given_args.host

port = given_args.port

filename = given_args.file

# First try-except block -- create socket

try:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

except socket.error, e:

print "Error creating socket: %s" % e

sys.exit(1)

# Second try-except block -- connect to given host/port

try:

s.connect((host, port))

except socket.gaierror, e:

print "Address-related error connecting to server: %s" % e

sys.exit(1)

except socket.error, e:

print "Connection error: %s" % e

sys.exit(1)

# Third try-except block -- sending data

try:

s.sendall("GET %s HTTP/1.0\r\n\r\n" % filename)

except socket.error, e:

print "Error sending data: %s" % e

sys.exit(1)

while 1:

# Fourth tr-except block -- waiting to receive data from remote host

try:

buf = s.recv(2048)

except socket.error, e:

print "Error receiving data: %s" % e

sys.exit(1)

if not len(buf):

break

# write the received data

sys.stdout.write(buf)

if __name__ == '__main__':

main()

1.8.2原理分析

在Python中,可以使用argparse模块把命令行参数传入脚本以及在脚本中解析命令行参数。这个模块在Python 2.7中可用。如果使用较旧版本的Python,这个模块可以到“Python包索引”(Python Package Index,简称PyPI)中获取,使用easy_install或pip安装。

这个攻略用到了三个命令行参数:主机名、端口号和文件名。上述脚本的使用方法如下:

$ python 1_7_socket_errors.py –host=

如果提供的主机不存在,这个脚本会输出如下错误:

$ python 1_7_socket_errors.py --host=www.pytgo.org --port=8080 --file=1_7_socket_errors.py

Address-related error connecting to server: [Errno -5] No address associated with hostname

如果某个端口上没有服务,你却尝试连接到这个端口,则这个脚本会抛出连接超时异常,如下所示:

$ python 1_7_socket_errors.py --host=www.python.org --port=8080 --file=1_7_socket_errors.py

这个命令会返回如下错误,因为主机www.python.org监听的不是端口8080:

Connection error: [Errno 110] Connection timed out

不过,如果向正确的主机、正确的端口发起随意的请求,应用层可能无法捕获这一异常。例如,运行下述脚本,不会返回错误,但输出的HTML代码说明了脚本的问题:

$ python 1_7_socket_errors.py --host=www.python.org --port=80 --file=1_7_socket_errors.py

HTTP/1.1 404 Not found

Server: Varnish

Retry-After: 0

content-type: text/html

Content-Length: 77

Accept-Ranges: bytes

Date: Thu, 20 Feb 2014 12:14:01 GMT

Via: 1.1 varnish

Age: 0

Connection: close

)

unknown domain: ))

这个攻略用到了四个try-except块。除第二个块处理socket.gaierror异常之外,其他块都处理socket.error异常。socket.gaierror是地址相关的错误。除此之外还有两种异常:socket.herror,C API中抛出的异常;如果在套接字中使用settimeout()方法,套接字超时后会抛出socket.timeout异常。

1.9修改套接字发送和接收的缓冲区大小

很多情况下,默认的套接字缓冲区大小可能不够用。此时,可以将默认的套接字缓冲区大小改成一个更合适的值。

1.9.1实战演练

我们要使用套接字对象的setsockopt()方法修改默认的套接字缓冲区大小。

首先,定义两个常量:SEND_BUF_SIZE和RECV_BUF_SIZE。然后在一个函数中调用套接字实例的setsockopt()方法。修改之前,最好先检查缓冲区大小是多少。注意,发送和接收的缓冲区大小要分开设定。

代码清单1-8展示了如何修改套接字的发送和接收缓冲区大小,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter – 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import socket

SEND_BUF_SIZE = 4096

RECV_BUF_SIZE = 4096

def modify_buff_size():

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM )

# Get the size of the socket's send buffer

bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)

print "Buffer size [Before]:%d" %bufsize

sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)

sock.setsockopt(

socket.SOL_SOCKET,

socket.SO_SNDBUF,

SEND_BUF_SIZE)

sock.setsockopt(

socket.SOL_SOCKET,

socket.SO_RCVBUF,

RECV_BUF_SIZE)

bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)

print "Buffer size [After]:%d" %bufsize

if __name__ == '__main__':

modify_buff_size()

运行上述脚本后,会显示修改套接字缓冲区大小前后的变化。根据你所用操作系统的本地设定,得到的输出可能有所不同:

$ python 1_8_modify_buff_size.py

Buffer size [Before]:16384

Buffer size [After]:8192

1.9.2原理分析

在套接字对象上可调用方法getsockopt()和setsockopt()分别获取和修改套接字对象的属性。setsockopt()方法接收三个参数:level、optname和value。其中,optname是选项名,value是该选项的值。第一个参数所用的符号常量(SO_*等)可在socket模块中查看。

1.10把套接字改成阻塞或非阻塞模式

默认情况下,TCP套接字处于阻塞模式中。也就是说,除非完成了某项操作,否则不会把控制权交还给程序。例如,调用connect() API后,连接操作会阻止程序继续往下执行,直到连接成功为止。很多情况下,你并不想让程序一直等待服务器响应或者有异常终止操作。例如,如果编写了一个网页浏览器客户端连接服务器,你应该考虑提供取消功能,以便在操作过程中取消连接。这时就要把套接字设置为非阻塞模式。

1.10.1实战演练

我们来看一下在Python中有哪些选项。在Python中,套接字可以被设置为阻塞模式或者非阻塞模式。在非阻塞模式中,调用API后,例如send()或recv()方法,如果遇到问题就会抛出异常。但在阻塞模式中,遇到错误并不会阻止操作。我们可以创建一个普通的TCP套接字,分别在阻塞模式和非阻塞模式中执行操作实验。

为了能在阻塞模式中处理套接字,首先要创建一个套接字对象。然后,调用setblocking(1)把套接字设为阻塞模式,或者调用setblocking(0)把套接字设为非阻塞模式。最后,把套接字绑定到指定的端口上,监听进入的连接。

代码清单1-9展示了如何把套接字设为阻塞模式或非阻塞模式,如下所示:

#!/usr/bin/env python

# Python Network Programming Cookbook -- Chapter - 1

# This program is optimized for Python 2.7. It may run on any

# other Python version with/without modifications.

import socket

def test_socket_modes():

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.setblocking(1)

s.settimeout(0.5)

s.bind(("127.0.0.1", 0))

socket_address = s.getsockname()

print "Trivial Server launched on socket: %s" %str(socket_address)

while(1):

s.listen(1)

if __name__ == '__main__':

test_socket_modes()

运行这个攻略后,会启动一个简易服务器,开启阻塞模式,如下述命令所示:

$ python 1_9_socket_modes.py

Trivial Server launched on socket: ('127.0.0.1', 51410)

1.10.2原理分析

在这个攻略中,我们把1传给setblocking()方法,启用套接字的阻塞模式。类似地,可以把0传给这个方法,把套接字设为非阻塞模式。

这个功能在后面的一些攻略中会用到,到时再详细说明其真正作用。

1.11重用套接字地址

不管连接是被有意还是无意关闭,有时你想始终在同一个端口上运行套接字服务器。某些情况下,如果客户端程序需要一直连接指定的服务器端口,这么做就很有用,因为无需改变服务器端口。

python网络编程项目_Python网络编程攻略相关推荐

  1. python网络编程项目_python网络编程详解

    最近在看<UNIX网络编程 卷1>和<FREEBSD操作系统设计与实现>这两本书,我重点关注了TCP协议相关的内容,结合自己后台开发的经验,写下这篇文章,一方面是为了帮助有需要 ...

  2. python网络编程项目_python网络编程(1):客户端与网络编程简介

    \n 301 Moved \nThe document has moved\nhere.\r\n

  3. python怎么转中文_Python中文全攻略【转】

    这几天一直纠结月python的中文编码问题,发现一篇不错的文章,转过来留个念想. 1.在Python中使用中文 在Python中有两种默认的字符串:str和unicode.在Python中一定要注意区 ...

  4. Python的IDE:基于Eclipse/MyEclipse软件的PyDev插件配置python的开发环境(不同python项目加载不同版本的python)—从而实现Python编程图文教程之详细攻略

    Python的IDE:基于Eclipse/MyEclipse软件的PyDev插件配置python的开发环境(不同python项目加载不同版本的python)-从而实现Python编程图文教程之详细攻略 ...

  5. Python:Python语言的简介(语言特点/pyc介绍/Python版本语言兼容问题(python2 VS Python3))、安装、学习路线(数据分析/机器学习/网页爬等编程案例分析)之详细攻略

    Python:Python语言的简介(语言特点/pyc介绍/Python版本语言兼容问题(python2 VS Python3)).安装.学习路线(数据分析/机器学习/网页爬等编程案例分析)之详细攻略 ...

  6. Java:计算机编程语言Java的简介、安装(编程环境/工具)、学习路线(如何学习Java以及几十项代码编程案例分析)之详细攻略

    Java:计算机编程语言Java的简介.安装(编程环境/工具).学习路线(如何学习Java以及几十项代码编程案例分析)之详细攻略 目录 Java的简介 1.Java的工作原理--基于Eclipse等编 ...

  7. DL之AlexNet:AlexNet算法的架构详解、损失函数、网络训练和学习之详细攻略

    DL之AlexNet:AlexNet算法的架构详解.损失函数.网络训练和学习之详细攻略 相关文章 Dataset:数据集集合(CV方向数据集)--常见的计算机视觉图像数据集大集合(建议收藏,持续更新) ...

  8. 2009.11网络工程师考试案例试题学习攻略(1)

    2009.11网络工程师考试案例试题学习攻略(1) 源于在网络工程实践过程中对该领域的喜爱,2005年本人有幸顺利地通过了网络工程师的资格考试,后又顺利地通过了系统分析师的资格考试.之后开始了软考培训 ...

  9. Flash:Flash动画设计软件界面的简介、Flash AS 3.0代码编程入门教程之详细攻略

    Flash:Flash动画设计软件界面的简介.Flash AS 3.0代码编程入门教程之详细攻略 目录 Flash动画设计软件界面的简介 快捷键 菜单栏 下边工具栏 右边工具栏 工具箱 Flash A ...

最新文章

  1. chrome/edge插件备忘
  2. CV:NVIDIA驱动程序安装图文教程(根据Anaconda的CUDA版本去安装对应匹配的NVIDIA)之详细攻略
  3. AndroidStudio通过JDBC连接MySQL数据库六大巨坑
  4. 使用DLL封装窗体和业务类
  5. Matlab的Floor, Ceil, Fix, Round
  6. JSP中Listener和Timer的运用
  7. Blazor University (7)组件 — 双向绑定
  8. Android系统性能优化(46)---Android Lint
  9. java 小数 乘法_javascript(js)的小数点乘法除法-Java架构师必看
  10. Xmind 8 下载以及破解
  11. 数据结构(二十) -- C语言版 -- 树 - 霍夫曼树(哈夫曼树、赫夫曼树、最优二叉树)、霍夫曼编码
  12. 将VMware与SoftICE基于网络的远程调试功能相结合
  13. win10基于QT开发手机安卓App
  14. 人工智能培训学校学哪些内容
  15. 下岗工冰城卖火“鱼豆腐”
  16. 为虚拟机配置静态ip地址
  17. 镭速软件如何使用文件同步功能?
  18. Excel如何一键改色
  19. java简单搭建分布式架构
  20. Linux SD卡驱动

热门文章

  1. 基于Echarts+HTML5可视化数据大屏展示—大数据智慧数据平台
  2. cas java单点登录_java单点登录系统CAS的简单使用
  3. oracle 怎么 制造崩溃,oracle数据库崩溃
  4. 省份城市区县三级联动html代码,基于Jquery实现省份、城市、区县三级联动
  5. 正则表达式的运算符优先级
  6. des java c_这个des加密,到底是用的哪种类型?
  7. 【OpenCV 例程200篇】94. 算术平均滤波器
  8. 【课题总结】OpenCV 抠图项目实战(1)目录摘要
  9. 华为手机显示解析服务器返回错误,ajax服务器返回错误
  10. vb microsoft.xmlhttp 获取所有超链接_编写我的第一个VB程序