使用 Socket 通信实现 FTP 客户端程序

2023-11-13

【转:】https://www.ibm.com/developerworks/cn/linux/l-cn-socketftp/index.html#FTP

FTP 概述

文件传输协议(FTP)作为网络共享文件的传输协议,在网络应用软件中具有广泛的应用。FTP的目标是提高文件的共享性和可靠高效地传送数据。

在传输文件时,FTP 客户端程序先与服务器建立连接,然后向服务器发送命令。服务器收到命令后给予响应,并执行命令。FTP 协议与操作系统无关,任何操作系统上的程序只要符合 FTP 协议,就可以相互传输数据。本文主要基于 LINUX 平台,对 FTP 客户端的实现原理进行详尽的解释并阐述如何使用 C 语言编写一个简单的 FTP 客户端。

FTP 协议

相比其他协议,如 HTTP 协议,FTP 协议要复杂一些。与一般的 C/S 应用不同点在于一般的C/S 应用程序一般只会建立一个 Socket 连接,这个连接同时处理服务器端和客户端的连接命令和数据传输。而FTP协议中将命令与数据分开传送的方法提高了效率。

FTP 使用 2 个端口,一个数据端口和一个命令端口(也叫做控制端口)。这两个端口一般是21 (命令端口)和 20 (数据端口)。控制 Socket 用来传送命令,数据 Socket 是用于传送数据。每一个 FTP 命令发送之后,FTP 服务器都会返回一个字符串,其中包括一个响应代码和一些说明信息。其中的返回码主要是用于判断命令是否被成功执行了。

命令端口

一般来说,客户端有一个 Socket 用来连接 FTP 服务器的相关端口,它负责 FTP 命令的发送和接收返回的响应信息。一些操作如“登录”、“改变目录”、“删除文件”,依靠这个连接发送命令就可完成。

数据端口

对于有数据传输的操作,主要是显示目录列表,上传、下载文件,我们需要依靠另一个 Socket来完成。

如果使用被动模式,通常服务器端会返回一个端口号。客户端需要用另开一个 Socket 来连接这个端口,然后我们可根据操作来发送命令,数据会通过新开的一个端口传输。

如果使用主动模式,通常客户端会发送一个端口号给服务器端,并在这个端口监听。服务器需要连接到客户端开启的这个数据端口,并进行数据的传输。

下面对 FTP 的主动模式和被动模式做一个简单的介绍。

主动模式 (PORT)

主动模式下,客户端随机打开一个大于 1024 的端口向服务器的命令端口 P,即 21 端口,发起连接,同时开放N +1 端口监听,并向服务器发出 “port N+1” 命令,由服务器从它自己的数据端口 (20) 主动连接到客户端指定的数据端口 (N+1)。

FTP 的客户端只是告诉服务器自己的端口号,让服务器来连接客户端指定的端口。对于客户端的防火墙来说,这是从外部到内部的连接,可能会被阻塞。

被动模式 (PASV)

为了解决服务器发起到客户的连接问题,有了另一种 FTP 连接方式,即被动方式。命令连接和数据连接都由客户端发起,这样就解决了从服务器到客户端的数据端口的连接被防火墙过滤的问题。

被动模式下,当开启一个 FTP 连接时,客户端打开两个任意的本地端口 (N > 1024 和 N+1) 。

第一个端口连接服务器的 21 端口,提交 PASV 命令。然后,服务器会开启一个任意的端口 (P > 1024 ),返回如“227 entering passive mode (127,0,0,1,4,18)”。 它返回了 227 开头的信息,在括号中有以逗号隔开的六个数字,前四个指服务器的地址,最后两个,将倒数第二个乘 256 再加上最后一个数字,这就是 FTP 服务器开放的用来进行数据传输的端口。如得到 227 entering passive mode (h1,h2,h3,h4,p1,p2),那么端口号是 p1*256+p2,ip 地址为h1.h2.h3.h4。这意味着在服务器上有一个端口被开放。客户端收到命令取得端口号之后, 会通过 N+1 号端口连接服务器的端口 P,然后在两个端口之间进行数据传输。

主要用到的 FTP 命令

FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令都以 "\r\n"结束。

要下载或上传一个文件,首先要登入 FTP 服务器,然后发送命令,最后退出。这个过程中,主要用到的命令有 USER、PASS、SIZE、REST、CWD、RETR、PASV、PORT、QUIT。

USER: 指定用户名。通常是控制连接后第一个发出的命令。“USER gaoleyi\r\n”: 用户名为gaoleyi 登录。

PASS: 指定用户密码。该命令紧跟 USER 命令后。“PASS gaoleyi\r\n”:密码为 gaoleyi。

SIZE: 从服务器上返回指定文件的大小。“SIZE file.txt\r\n”:如果 file.txt 文件存在,则返回该文件的大小。

CWD: 改变工作目录。如:“CWD dirname\r\n”。

PASV: 让服务器在数据端口监听,进入被动模式。如:“PASV\r\n”。

PORT: 告诉 FTP 服务器客户端监听的端口号,让 FTP 服务器采用主动模式连接客户端。如:“PORT h1,h2,h3,h4,p1,p2”。

RETR: 下载文件。“RETR file.txt \r\n”:下载文件 file.txt。

STOR: 上传文件。“STOR file.txt\r\n”:上传文件 file.txt。

REST: 该命令并不传送文件,而是略过指定点后的数据。此命令后应该跟其它要求文件传输的 FTP 命令。“REST 100\r\n”:重新指定文件传送的偏移量为 100 字节。

QUIT: 关闭与服务器的连接。

FTP 响应码

客户端发送 FTP 命令后,服务器返回响应码。

响应码用三位数字编码表示:

第一个数字给出了命令状态的一般性指示,比如响应成功、失败或不完整。

第二个数字是响应类型的分类,如 2 代表跟连接有关的响应,3 代表用户认证。

第三个数字提供了更加详细的信息。

第一个数字的含义如下:

1 表示服务器正确接收信息,还未处理。

2 表示服务器已经正确处理信息。

3 表示服务器正确接收信息,正在处理。

4 表示信息暂时错误。

5 表示信息永久错误。

第二个数字的含义如下:

0 表示语法。

1 表示系统状态和信息。

2 表示连接状态。

3 表示与用户认证有关的信息。

4 表示未定义。

5 表示与文件系统有关的信息。

Socket 编程的几个重要步骤

Socket 客户端编程主要步骤如下:

  1. socket() 创建一个 Socket
  2. connect() 与服务器连接
  3. write() 和 read() 进行会话
  4. close() 关闭 Socket

Socket 服务器端编程主要步骤如下:

  1. socket() 创建一个 Socket
  2. bind()
  3. listen() 监听
  4. accept() 接收连接的请求
  5. write() 和 read() 进行会话
  6. close() 关闭 Socket

实现 FTP 客户端上传下载功能

下面让我们通过一个例子来对 FTP 客户端有一个深入的了解。本文实现的 FTP 客户端有下列功能:

  1. 客户端和 FTP 服务器建立 Socket 连接。
  2. 向服务器发送 USER、PASS 命令登录 FTP 服务器。
  3. 使用 PASV 命令得到服务器监听的端口号,建立数据连接。
  4. 使用 RETR/STOR 命令下载/上传文件。
  5. 在下载完毕后断开数据连接并发送 QUIT 命令退出。

本例中使用的 FTP 服务器为 filezilla。在整个交互的过程中,控制连接始终处于连接的状态,数据连接在每传输一个文件时先打开,后关闭。

客户端和 FTP 服务器建立 Socket 连接

当客户端与服务器建立连接后,服务器会返回 220 的响应码和一些欢迎信息。

图 1. 客户端连接到服务器端

图 1. 客户端连接到服务器端

清单 1. 客户端连接到 FTP 服务器,接收欢迎信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

SOCKET control_sock;

struct hostent *hp;

struct sockaddr_in server;

memset(&server, 0, sizeof(struct sockaddr_in));

 

/* 初始化socket */

control_sock = socket(AF_INET, SOCK_STREAM, 0);

hp = gethostbyname(server_name);

memcpy(&server.sin_addr, hp->h_addr, hp->h_length);

server.sin_family = AF_INET;

server.sin_port = htons(port);

 

/* 连接到服务器端 */

connect(control_sock,(struct sockaddr *)&server, sizeof(server));

/* 客户端接收服务器端的一些欢迎信息 */

read(control_sock, read_buf, read_len);

客户端登录 FTP 服务器

当客户端发送用户名和密码,服务器验证通过后,会返回 230 的响应码。然后客户端就可以向服务器端发送命令了。

图 2. 客户端登录 FTP 服务器

客户端登录 FTP 服务器

清单 2. 客户端发送用户名和密码,登入 FTP 服务器

1

2

3

4

5

6

7

8

9

10

11

12

13

/* 命令 ”USER username\r\n” */

sprintf(send_buf,"USER %s\r\n",username);

/*客户端发送用户名到服务器端 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”331 User name okay, need password.” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”PASS password\r\n” */

sprintf(send_buf,"PASS %s\r\n",password);

/* 客户端发送密码到服务器端 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”230 User logged in, proceed.” */

read(control_sock, read_buf, read_len);

客户端让 FTP 服务器进入被动模式

当客户端在下载/上传文件前,要先发送命令让服务器进入被动模式。服务器会打开数据端口并监听。并返回响应码 227 和数据连接的端口号。

图 3. 客户端让服务器进入被动模式

图 3. 客户端让服务器进入被动模式

清单 3. 让服务器进入被动模式,在数据端口监听

1

2

3

4

5

6

7

/* 命令 ”PASV\r\n” */

sprintf(send_buf,"PASV\r\n");

/* 客户端告诉服务器用被动模式 */

write(control_sock, send_buf, strlen(send_buf));

/*客户端接收服务器的响应码和新开的端口号,

* 正常为 ”227 Entering passive mode (<h1,h2,h3,h4,p1,p2>)” */

read(control_sock, read_buf, read_len);

客户端通过被动模式下载文件

当客户端发送命令下载文件。服务器会返回响应码 150,并向数据连接发送文件内容。

图 4. 客户端从FTP服务器端下载文件

图 4. 客户端从FTP服务器端下载文件

清单 4. 客户端连接到 FTP 服务器的数据端口并下载文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

/* 连接服务器新开的数据端口 */

connect(data_sock,(struct sockaddr *)&server, sizeof(server));

/* 命令 ”CWD dirname\r\n” */

sprintf(send_buf,"CWD %s\r\n", dirname);

/* 客户端发送命令改变工作目录 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”250 Command okay.” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”SIZE filename\r\n” */

sprintf(send_buf,"SIZE %s\r\n",filename);

/* 客户端发送命令从服务器端得到下载文件的大小 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”213 <size>” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”RETR filename\r\n” */

sprintf(send_buf,"RETR %s\r\n",filename);

/* 客户端发送命令从服务器端下载文件 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”150 Opening data connection.” */

read(control_sock, read_buf, read_len);

 

/* 客户端创建文件 */

file_handle = open(disk_name, CRFLAGS, RWXALL);

for( ; ; ) {

... ...

/* 客户端通过数据连接 从服务器接收文件内容 */

read(data_sock, read_buf, read_len);

/* 客户端写文件 */

write(file_handle, read_buf, read_len);

... ...

}

/* 客户端关闭文件 */

rc = close(file_handle);

客户端退出服务器

当客户端下载完毕后,发送命令退出服务器,并关闭连接。服务器会返回响应码 200。

图 5. 客户端从 FTP 服务器退出

图 5. 客户端从 FTP 服务器退出

清单 5. 客户端关闭数据连接,退出 FTP 服务器并关闭控制连接

1

2

3

4

5

6

7

8

9

10

11

12

13

/* 客户端关闭数据连接 */

close(data_sock);

/* 客户端接收服务器的响应码和信息,正常为 ”226 Transfer complete.” */

read(control_sock, read_buf, read_len);

 

/* 命令 ”QUIT\r\n” */

sprintf(send_buf,"QUIT\r\n");

/* 客户端将断开与服务器端的连接 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码,正常为 ”200 Closes connection.” */

read(control_sock, read_buf, read_len);

/* 客户端关闭控制连接 */

close(control_sock);

至此,下载文件已经完成。需要注意的是发送 FTP 命令的时候,在命令后要紧跟 “\r\n”,否则服务器不会返回信息。回车换行符号 “\r\n” 是 FTP 命令的结尾符号,当服务器接收到这个符号时,认为客户端发送的命令已经结束,开始处理。否则会继续等待。

让我们来看一下 FTP 服务器这一端的响应情况:

清单 6. 客户端下载文件时,FTP 服务器的响应输出

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

(not logged in) (127.0.0.1)> Connected, sending welcome message...

(not logged in) (127.0.0.1)> 220-FileZilla Server version 0.9.36 beta

(not logged in) (127.0.0.1)> 220 hello gaoleyi

(not logged in) (127.0.0.1)> USER gaoleyi

(not logged in) (127.0.0.1)> 331 Password required for gaoleyi

(not logged in) (127.0.0.1)> PASS *********

gaoleyi (127.0.0.1)> 230 Logged on

gaoleyi (127.0.0.1)> PWD

gaoleyi (127.0.0.1)> 257 "/" is current directory.

gaoleyi (127.0.0.1)> SIZE file.txt

gaoleyi (127.0.0.1)> 213 4096

gaoleyi (127.0.0.1)> PASV

gaoleyi (127.0.0.1)> 227 Entering Passive Mode (127,0,0,1,13,67)

gaoleyi (127.0.0.1)> RETR file.txt

gaoleyi (127.0.0.1)> 150 Connection accepted

gaoleyi (127.0.0.1)> 226 Transfer OK

gaoleyi (127.0.0.1)> QUIT

gaoleyi (127.0.0.1)> 221 Goodbye

首先,服务器准备就绪后返回 220。客户端接收到服务器端返回的响应码后,相继发送“USER username” 和 “PASS password” 命令登录。随后,服务器返回的响应码为 230 开头,说明客户端已经登入了。这时,客户端发送 PASV 命令让服务器进入被动模式。服务器返回如 “227 Entering Passive Mode (127,0,0,1,13,67)”,客户端从中得到端口号,然后连接到服务器的数据端口。接下来,客户端发送下载命令,服务器会返回响应码 150,并从数据端口发送数据。最后,服务器返回 “226 transfer complete”,表明数据传输完成。

需要注意的是,客户端不要一次发送多条命令,例如我们要打开一个目录并且显示这个目录,我们得发送 CWD dirname,PASV,LIST。在发送完 CWD dirname 之后等待响应代码,然后再发送后面一条。当 PASV 返回之后,我们打开另一个 Socket 连接到相关端口上。然后发送 LIST,返回 125 之后在开始接收数据,最后返回 226 表明完成。

在传输多个文件的过程中,需要注意的是每次新的传输都必须重新使用 PASV 获取新的端口号,接收完数据后应该关闭该数据连接,这样服务器才会返回一个 2XX 成功的响应。然后客户端可以继续下一个文件的传输。

上传文件与下载文件相比,登入验证和切换被动模式都如出一辙,只需要改变发送到服务器端的命令,并通过数据连接发送文件内容。

客户端通过被动模式向服务器上传文件

当客户端发送命令上传文件,服务器会从数据连接接收文件。

图 6. 客户端连接到 FTP 服务器的数据端口并上传文件

客户端连接到 FTP 服务器的数据端口并上传文件

客户端通过主动模式向服务器上传文件

到目前为止,本文介绍的都是客户端用被动模式进行文件的上传和下载。下面将介绍客户端用主动模式下载文件。

图 7. 用主动模式从 FTP 服务器下载文件

用主动模式从 FTP 服务器下载文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

清单 7. 用主动模式从 FTP 服务器下载文件的示例 C 程序

... ...

SOCKET data_sock;

data_sock = socket(AF_INET, SOCK_STREAM, 0);

struct  sockaddr_in  name;

name.sin_family = AF_INET;

name.sin_addr.s_addr = htons(INADDR_ANY);

server_port = p1*256+p2;

length = sizeof(name);

name.sin_port = htons(server_port);

bind(server_sock, (struct sockaddr *)&name, length);

struct  sockaddr_in client_name;

length = sizeof(client_name);

 

/* 客户端开始监听端口p1*256+p2 */

listen(server_sock, 64);

 

/* 命令 ”PORT \r\n” */

sprintf(send_buf,"PORT 1287,0,0,1,%d,%d\r\n", p1, p2);

write(control_sock, send_buf,strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”200 Port command successful” */

read(control_sock, read_buf, read_len);

 

sprintf(send_buf,"RETR filename.txt\r\n");

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,正常为 ”150 Opening data channel for file transfer.” */

read(control_sock, read_buf, read_len);

 

/* ftp客户端接受服务器端的连接请求 */

data_sock = accept(server_sock,(struct sockaddr *)&client_name, &length);

... ...

 

file_handle = open(disk_name, ROFLAGS, RWXALL);

for( ; ; ) {

... ...

read(data_sock, read_buf, read_len);

write(file_handle, read_buf, read_len);

... ...

}

close(file_handle);

客户端通过 PORT 命令告诉服务器连接自己的 p1*256+p2 端口。随后在这个端口进行监听,等待 FTP 服务器连接上来, 再通过这个数据端口来传输文件。PORT 方式在传送数据时,FTP 客户端其实就相当于一个服务器端,由 FTP 服务器主动连接自己。

断点续传

由于网络不稳定,在传输文件的过程中,可能会发生连接断开的情况,这时候需要客户端支持断点续传的功能,下次能够从上次终止的地方开始接着传送。需要使用命令 REST。如果在断开连接前,一个文件已经传输了 512 个字节。则断点续传开始的位置为 512,服务器会跳过传输文件的前 512 字节。

清单 8. 从 FTP 服务器断点续传下载文件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

... ...

/* 命令 ”REST offset\r\n” */

sprintf(send_buf,"REST %ld\r\n", offset);

/* 客户端发送命令指定下载文件的偏移量 */

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,

*正常为 ”350 Restarting at <position>. Send STORE or RETRIEVE to initiate transfer.” */

read(control_sock, read_buf, read_len);

... ...

 

/* 命令 ”RETR filename\r\n” */

sprintf(send_buf,"RETR %s\r\n",filename);

/* 客户端发送命令从服务器端下载文件, 并且跳过该文件的前offset字节*/

write(control_sock, send_buf, strlen(send_buf));

/* 客户端接收服务器的响应码和信息,*

*正常为 ”150 Connection accepted, restarting at offset <position>” */

read(control_sock, read_buf, read_len);

... ...

 

file_handle = open(disk_name, CRFLAGS, RWXALL);

/* 指向文件写入的初始位置 */

lseek(file_handle, offset, SEEK_SET);

... ...

结束语

本文从应用实现的角度,介绍了 FTP 协议。并用详尽的例子分析了如何用主动模式和被动模式实现 FTP 客户端上传下载文件,如何进行断点续传。通过本文可以让读者对 FTP 客户端的原理有一个深入的了解。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Socket 通信实现 FTP 客户端程序 的相关文章

  • 如何从Firebase Firestore实时更新文档中获取修改后的字段或数据? [复制]

    这个问题在这里已经有答案了 我有多个文档 我的问题是我无法获取修改的特定数据 我正在获取完整的文档 db collection employees whereEqualTo OID OID addSnapshotListener new E
  • 获取文件的锁

    我想在对特定文件开始 threo read 时获取文件上的锁定 以便其他应用程序无法读取已锁定的文件并希望在线程终止时释放锁定文件 您可以获得一个FileLock https docs oracle com javase 8 docs ap
  • Base36 编码字符串?

    我一直在网上查找 但找不到解决此问题的方法 在 Python Ruby 或 Java 中 如何对以下字符串进行 Base 36 编码 nOrG9Eh0uyeilM8Nnu5pTywj3935kW 5 Ruby 以 36 为基数 s unpa
  • 将SQL数据引入jquery availabletag

    我正在尝试制作自动完成文本框 但如何将 SQL 数据包含到 jquery 可用标记并循环它 我无法根据以下代码执行该功能 任何帮助 将不胜感激 谢谢 这是我的预期输出 预期结果演示 http jsfiddle net VvETA 71 jq
  • 从 MS Access 中提取 OLE 对象(Word 文档)

    我有一个 Microsoft Access 数据库 其中包含一个包含 Microsoft Word 文档的 OLE 对象字段 我试图找到代码来检索保存在 OLE 对象中的文件 以便用户可以从我的 JavaFx 应用程序中的按钮下载它 但没有
  • 如何在java Spring Boot中实现通用服务类?

    我有许多具有重复代码的服务 我想知道如何实现通用服务 以便我的所有服务都可以扩展它 服务接口示例 重复代码 Service public interface IUserService List
  • 为什么Iterator接口没有add方法

    In IteratorSun 添加了remove 方法来删 除集合中最后访问的元素 为什么没有add方法来向集合中添加新元素 它可能对集合或迭代器产生什么样的副作用 好的 我们开始吧 设计常见问题解答中明确给出了答案 为什么不提供 Iter
  • Android蓝牙java.io.IOException:bt套接字已关闭,读取返回:-1

    我正在尝试编写一个代码 仅连接到运行 Android 5 0 KitKat 的设备上的 目前 唯一配对的设备 无论我尝试了多少方法 我仍然会收到此错误 这是我尝试过的最后一个代码 它似乎完成了我看到人们报告为成功的所有事情 有人能指出我做错
  • 虽然我的类已加载,但 Class.forName 抛出 ClassNotFoundException

    代码如下 它的作用是加载我放在主目录中的 jar 文件中的所有类 import java io File import java util jar JarFile import java util jar JarEntry import j
  • 如何从日期中删除毫秒、秒、分钟和小时[重复]

    这个问题在这里已经有答案了 我遇到了一个问题 我想比较两个日期 然而 我只想比较年 月 日 这就是我能想到的 private Date trim Date date Calendar calendar Calendar getInstanc
  • 在 Clojure 中解压缩 zlib 流

    我有一个二进制文件 其内容由zlib compress在Python上 有没有一种简单的方法可以在Clojure中打开和解压缩它 import zlib import json with open data json zlib wb as
  • 无法在 Java/Apache HttpClient 中处理带有垂直/管道栏的 url

    例如 如果我想处理这个网址 post new HttpPost http testurl com lists lprocess action LoadList 401814 1 Java Apache 不允许我这么做 因为它说竖线 是非法的
  • Java - 从 XML 文件读取注释

    我必须从 XML 文件中提取注释 我找不到使用 JDOM 或其他东西来让它们使用的方法 目前我使用 Regex 和 FileReader 但我不认为这是正确的方法 您可以使用 JDOM 之类的东西从 XML 文件中获取注释吗 或者它仅限于元
  • 避免 Java 中的重复导入:继承导入?

    有没有办法 继承 导入 Example 常见枚举 public enum Constant ONE TWO THREE 使用此枚举的基类 public class Base protected void register Constant
  • 如何从 Ant 启动聚合 jetty-server JAR?

    背景 免责声明 I have veryJava 经验很少 我们之前在 Ant 构建期间使用了 Jetty 6 的包装版本来处理按需静态内容 JS CSS 图像 HTML 因此我们可以使用 PhantomJS 针对 HTTP 托管环境运行单元
  • Hadoop NoSuchMethodError apache.commons.cli

    我在用着hadoop 2 7 2我用 IntelliJ 做了一个 MapReduce 工作 在我的工作中 我正在使用apache commons cli 1 3 1我把库放在罐子里 当我在 Hadoop 集群上使用 MapReduceJob
  • 何时在 hibernate 中使用 DiscriminatorValue 注解

    在 hibernate 中使用 DiscriminatorValue 注释的最佳场景是什么以及何时 这两个链接最能帮助我理解继承概念 http docs oracle com javaee 6 tutorial doc bnbqn html
  • Java 的 PriorityQueue 与最小堆有何不同?

    他们为什么命名PriorityQueue如果你不能插入优先级 它看起来与堆非常相似 有什么区别吗 如果没有区别那为什么叫它PriorityQueue而不是堆 默认的PriorityQueue是用Min Heap实现的 即栈顶元素是堆中最小的
  • 检查应用程序是否在 Android Market 上可用

    给定 Android 应用程序 ID 包名称 如何以编程方式检查该应用程序是否在 Android Market 上可用 例如 com rovio angrybirds 可用 而 com random app ibuilt 不可用 我计划从
  • ArrayList.clear() 和 ArrayList.removeAll() 有什么区别?

    假如说arraylist定义为ArrayList

随机推荐

  • 随机生User-Agent代理Ip

    import random import urllib request def url url p 49 235 246 24 8118 proxy support urllib request ProxyHandler http p op
  • opencv2 无法加载RTSP的问题

    最近通过OPENCV3 2 0加载海康摄像头 实现后续的摄像头画面拼接 但是发现拷贝了所有的opencv2的dll文件 仍然无法成功加载rtsp视频流 假设是视频流无法获取 通过VLC工具拉流 可以成功 效果如下 后来试了很多其他办法 感觉
  • linux卸载内核

    查看所安装的所有内核 ls boot 在删除旧内核之前 记住最好留有2个最近的内核 最新的和上一个版本 以防主要的版本出错 现在就让我们看看如何在Ubuntu上清理旧内核 在Ubuntu内核镜像包含了以下的包 linux image 内核镜
  • 【python实现华为OD机试真题】优雅子数组【2023 Q1

    题目描述 如果一个数组Q中出现次数最多的元素出现大于等于K次 被称为k 优雅数组 k也可以被称为优雅阈值只 例如 数组1 2 3 1 2 3 1 它是一个3 优雅数组 因为元素1出现次数大于等于3次 数组 1 2 3 1 2 就不是一一个3
  • UE4 跑酷游戏-得分机制

    得分机制 1 这次我们要做的是得分 根据人物跑过的地板来得取分数 首先进入我们的第三人称游戏模式里面 创建一些变量 2 退出第三人称游戏模式后 进入主地板蓝图后 在销毁地板之前写读取人物跑i过地板块数进行得分
  • Unity 使用Photon Server 联网开发(一)配置连接设置流程

    Photon官网首页 Photon官网 下载配置Pun与Server的教程 Photon Pun与Server的下载与配置教程 1 Pun导入项目后配置PhotonServerSettings Hosting 服务器托管方式 Not Set
  • Date 日期时间 浅层研究

    Date 日期时间 浅层研究 若需查看所有函数及属性常量 请直接翻至尾部 文章目录 Date 日期时间 浅层研究 获取当前时间 奇怪玩法 Date System Calendar 奇怪的问题探究区 查看此资料之前我们需要了解下 native
  • 论文阅读:FMCW雷达生命体征监测(心跳监测)

    论文 基于连续波雷达的非接触式生命体征监测系统设计与实现 1 雷达种类及特点 用于非接触式测量的雷达种类主要有脉冲雷达和连续波雷达两类 脉冲雷达根据发射脉冲与接收脉冲的时间差来计算所测量对象的实际距离 脉冲雷达需要将窄脉冲持续地发出 其优点
  • QVector 容器

    QVector 容器 QVector在相邻的内存中存储给定数据类型T的一组数据 在QVector前部或中间位置插入操作速度都很慢 因会导致内存中大量的数据移动 访问数据可使用下标 也可使用迭代器 继承自QVector类的子类有QPolygo
  • 如何运行后缀名为.ipynb的文件

    打开cmd 输入 pip install jupyter notebook 安装截图 下载之后 输入 jupyter notebook 之后浏览器会弹出一个页面 如图 然后就可以打开电脑里的文件 如图 也可以选择upload你的文件 打开i
  • Android 下拉刷新实践

    1 手动实现一个下拉刷新功能 2 效果图 3 view结构 4 实现思路
  • linux系统调用线程

    1 基础概念 早期unix系统中 没有线程概念 后来才引入线程 linxu 为了迎合 windows引入了线程 linux 上进程是非常优秀了 linux 上用线程和进程的区别不大 老程序都是用进程 gdb不支持线程 因为gdb比线程出现了
  • net core 下的图形验证码

    首先 通过 Nuget 安装 dotnet add package Lazy Captcha Core 注册服务 默认使用了内存存储 AddDistributedMemoryCache builder Services AddCaptcha
  • 什么是IDP?---What Is an Internal Developer Platform (IDP)?

    The modern approach to software delivery is based on cloud native services and the DevOps culture entailing software dev
  • 项目-天气邮局

    一 项目背景 http协议被广泛使用 从移动端 pc端浏览器 http协议无疑是打开互联网应用窗口的重要协议 http在网络应用层中的地位不可撼动 是能准确区分前后台的重要协议 在学习完网络的有关知识后 HTTP服务器无疑是巩固及应用所学知
  • 怎么用linux查看xml文件格式,xml是什么格式?xml文件格式用什么软件可以打开

    xml是什么格式 xml文件是很多用户在电脑上看见过了 很多小伙伴看到了xml格式的文件都不知道这个是什么东东 其实这个xml也是一种比较有用的文件 可以用来存储软件数据 不过不是所有的软件都可以打开的 下面智能手机网就来科普一下xml是什
  • 各种虚拟机体验杂谈 --- 兼发布 google chrome os (chromiumos) vmware版本

    前两天赶时髦 把笔记本换上了win8 pro 换win8pro的原因 一个是价格真的很有诚意 另一个就是从DP版本开始就一直用 虽然兼容性问题多多 但作为宿主主机还行 而且xenclient也实在是让人窝火 号称裸机虚拟 其实硬盘速度慢如蜗
  • 读论文(五)MedDialog【参考性大】【可复现】

    Abstract 医疗对话系统有望帮助远程医疗增加医疗保健服务的可及性 提高患者护理质量并降低医疗成本 为促进医学对话系统的研发 我们构建了大规模的医学对话数据集 MedDialog 其中包含中文数据集340万条医患对话 英文数据集120条
  • 24 个 ES6 方法,解决实际开发的 JS 问题

    1 如何隐藏所有指定的元素 tips 本文主要介绍 24 中 es6 方法 这些方法都挺实用的 本本请记好 时不时翻出来看看 const hide el gt Array from el forEach e gt e style displ
  • 使用 Socket 通信实现 FTP 客户端程序

    转 https www ibm com developerworks cn linux l cn socketftp index html FTP FTP 概述 文件传输协议 FTP 作为网络共享文件的传输协议 在网络应用软件中具有广泛的应