-
2021-05-23 08:33:57
FTP 与大多数 Internet 服务一样,使用的也是“客户端/服务器”模式。
用户通过一个支持 FTP 协议的客户机程序,连接在远程主机上的 FTP 服务器程序。通过在客户端向服务器端发送 FTP 命令,服务器执行该命令,并将执行结果返回给客户端。由于“控制连接”的因素,客户端发送的 FTP 命令,服务器都会有对应的应答。
FTP 工作流程如图所示。
上图中显示了 FTP 进行文件传输的基本工作流程。主要分为 4 个阶段,即建立连接阶段、身份认证阶段、命令交互阶段和断开连接阶段。下面对这 4 个阶段进行详细介绍。
建立连接阶段
该阶段是 FTP 客户端通过 TCP 三次握手与FTP服务器端进行建立连接。
客户端向 FTP 服务器发出建立连接请求,FTP 服务器对请求进行应答。如果 FTP 服务器上的 21 端口是启用的,可以接受来自其他主机的请求,给出应答 220,表示服务就绪,即告诉客户端需要的 FTP 服务已经准备好了。
返回应答以后,FTP 服务器需要客户端进行身份认证,向客户端发送身份认证请求。
身份认证阶段
身份认证是指客户端需要向FTP服务提供登录所需的用户名和密码。FTP 服务器对客户端输入的用户名和密码都会给出相应的应答。如果客户端输入的用户名和密码正确,将成功登录FTP服务器,此时进入 FTP 会话。
命令交互阶段
在 FTP 会话中,用户可以执行 FTP 命令进行文件传输,如查看目录信息、上传或下载文件等。客户端输入要执行的 FTP 命令后,服务器同样会给出应答。如果输入的执命令正确,服务器会将命令的执行结果返回给客户端。执行结果返回完成后,服务器继续给出应答。
断开连接阶段
当客户端不再与 FTP 服务器进行文件传输时,需要断开连接。客户端向 FTP 服务器发送断开连接请求,服务器收到断开连接后给出相应的应答。
验证工作流程
为了能够更好地理解FTP客户端与服务器的工作流程,下面以一个简单的实例进行讲解。
【实例】已知 FTP 服务器的 IP 地址为 192.168.59.135。使用一个主机作为 FTP 客户端与 FTP 服务器进行文件传输。
这里查看 FTP 服务器上目录 content 中的信息。执行命令如下:
root@daxueba:~# ftp
为了方便分析,下面将按照 FIP 的 4 个工作流程阶段进行分析。
建立连接
ftp> open 192.168.59.135 #客户端请求建立连接
Connected to 192.168.59.135.
220 Welcome #服务器应答, 应答码为220
以上输出信息显示客户端向 FTP 服务器 192.168.59.135 发起了连接请求,服务器给出了对应的应答码 220,表示成功建立了连接。
身份认证
Name (192.168.59.135:root): sm #客户端输入的用户名(明文显示), 这里为sm
331 User name ok, need password. #服务器应答, 应答码为331
Password: #客户端输入的密码
230 User logged in, proceed. #服务器应答, 应答码为230
以上输出信息显示了客户端输入的用户名和密码,并显示了对应的应答码。用户名的应答码为 331,表示还需要客户端输入密码;密码的应答码为 230,表示登录成功。
命令交互
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir content #客户端执行的命令
200 Connected. #服务器应答, 应答码为200
150 Start of file list. #服务器应答, 应答码为150
-rwxrwxrwx 1 ftp ftp 18 Sep 11 14:47 file-3.txt #目录列表信息
-rwxrwxrwx 1 ftp ftp 18 Sep 11 14:47 file-1.txt #目录列表信息
-rwxrwxrwx 1 ftp ftp 18 Sep 11 14:47 file-2.txt #目录列表信息
226 Transfer complete. #服务器应答, 应答码为226
以上输出信息显示了用户名要执行的命令 dir content,表示显示 FTP 服务器上 content 的目录列表信息;服务器给出了应答,这里给出了两个应答码。应答码为 200,表示 FTP 服务器可以执行该命令;应答码为 150,表示服务器已经准备好了目录列表,打开数据连接,将执行结果发送给客户端。
这里显示的目录列表信息可以看到 content 中有 3 个文件 file-1.txt,file-2.txt 和 file-3.txt。输出信息最后一行显示了服务器的应答码 226,表示执行结果已经返回。
断开连接
ftp> quit #客户端指定的断开FTP子命令
221 Service closing control connection. #服务器应答, 应答码为221
以上输出信息显示了客户端执行的断开连接的 FTP 子命令 quit。最后一行显示了服务器的应答,应答码为 221,表示服务器确认退出登录。
更多相关内容 -
用标准C语言编写一个ftp协议上传文件的简单程序
2021-05-19 10:07:54/* vi: set sw=4 ts=4: *//* -------------------------------------------------------------------------* tftp.c** A simple tftp client for busybox.* Tries to follow RFC1350.* Only "octet" mode supported..../* vi: set sw=4 ts=4: */
/* -------------------------------------------------------------------------
* tftp.c
*
* A simple tftp client for busybox.
* Tries to follow RFC1350.
* Only "octet" mode supported.
* Optional blocksize negotiation (RFC2347 + RFC2348)
*
* Copyright (C) 2001 Magnus Damm
*
* Parts of the code based on:
*
* atftp: Copyright (C) 2000 Jean-Pierre Lefebvre
* and Remi Lefebvre
*
* utftp: Copyright (C) 1999 Uwe Ohse
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
* ------------------------------------------------------------------------- */
#include "libbb.h"
#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
#define TFTP_TIMEOUT 5 /* seconds */
#define TFTP_NUM_RETRIES 5 /* number of retries */
/* opcodes we support */
#define TFTP_RRQ 1
#define TFTP_WRQ 2
#define TFTP_DATA 3
#define TFTP_ACK 4
#define TFTP_ERROR 5
#define TFTP_OACK 6
#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
#define USE_GETPUT(...)
#define CMD_GET(cmd) 1
#define CMD_PUT(cmd) 0
#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
#define USE_GETPUT(...)
#define CMD_GET(cmd) 0
#define CMD_PUT(cmd) 1
#else
#define USE_GETPUT(...) __VA_ARGS__
/* masks coming from getpot32 */
#define CMD_GET(cmd) ((cmd) & 1)
#define CMD_PUT(cmd) ((cmd) & 2)
#endif
/* NB: in the code below
* CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
*/
#if ENABLE_FEATURE_TFTP_BLOCKSIZE
static int tftp_blocksize_check(int blocksize, int bufsize)
{
/* Check if the blocksize is valid:
* RFC2348 says between 8 and 65464,
* but our implementation makes it impossible
* to use blocksizes smaller than 22 octets.
*/
if ((bufsize && (blocksize > bufsize))
|| (blocksize < 8) || (blocksize > 65564)
) {
bb_error_msg("bad blocksize");
return 0;
}
return blocksize;
}
static char *tftp_option_get(char *buf, int len, const char *option)
{
int opt_val = 0;
int opt_found = 0;
int k;
while (len > 0) {
/* Make sure the options are terminated correctly */
for (k = 0; k < len; k++) {
if (buf[k] == '\0') {
goto nul_found;
}
}
return NULL;
nul_found:
if (opt_val == 0) {
if (strcasecmp(buf, option) == 0) {
opt_found = 1;
}
} else if (opt_found) {
return buf;
}
k++;
buf += k;
len -= k;
opt_val ^= 1;
}
return NULL;
}
#endif
static int tftp( USE_GETPUT(const int cmd,)
len_and_sockaddr *peer_lsa,
const char *remotefile, const int localfd,
unsigned port, int tftp_bufsize)
{
struct timeval tv;
fd_set rfds;
int socketfd;
int len;
int send_len;
USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
smallint finished = 0;
uint16_t opcode;
uint16_t block_nr = 1;
uint16_t recv_blk;
int timeout = TFTP_NUM_RETRIES;
char *cp;
unsigned org_port;
len_and_sockaddr *const from = alloca(offsetof(len_and_sockaddr, sa) + peer_lsa->len);
/* Can't use RESERVE_CONFIG_BUFFER here since the allocation
* size varies meaning BUFFERS_GO_ON_STACK would fail */
/* We must keep the transmit and receive buffers seperate */
/* In case we rcv a garbage pkt and we need to rexmit the last pkt */
char *xbuf = xmalloc(tftp_bufsize += 4);
char *rbuf = xmalloc(tftp_bufsize);
port = org_port = htons(port);
socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
/* build opcode */
opcode = TFTP_WRQ;
if (CMD_GET(cmd)) {
opcode = TFTP_RRQ;
}
cp = xbuf + 2;
/* add filename and mode */
/* fill in packet if the filename fits into xbuf */
len = strlen(remotefile) + 1;
if (2 + len + sizeof("octet") >= tftp_bufsize) {
bb_error_msg("remote filename is too long");
goto ret;
}
strcpy(cp, remotefile);
cp += len;
/* add "mode" part of the package */
strcpy(cp, "octet");
cp += sizeof("octet");
#if ENABLE_FEATURE_TFTP_BLOCKSIZE
len = tftp_bufsize - 4; /* data block size */
if (len != TFTP_BLOCKSIZE_DEFAULT) {
/* rfc2348 says that 65464 is a max allowed value */
if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) {
bb_error_msg("remote filename is too long");
goto ret;
}
/* add "blksize", , blocksize */
strcpy(cp, "blksize");
cp += sizeof("blksize");
cp += snprintf(cp, 6, "%d", len) + 1;
want_option_ack = 1;
}
#endif
/* First packet is built, so skip packet generation */
goto send_pkt;
/* Using mostly goto's - continue/break will be less clear
* in where we actually jump to */
while (1) {
/* Build ACK or DATA */
cp = xbuf + 2;
*((uint16_t*)cp) = htons(block_nr);
cp += 2;
block_nr++;
opcode = TFTP_ACK;
if (CMD_PUT(cmd)) {
opcode = TFTP_DATA;
len = full_read(localfd, cp, tftp_bufsize - 4);
if (len < 0) {
bb_perror_msg(bb_msg_read_error);
goto ret;
}
if (len != (tftp_bufsize - 4)) {
finished = 1;
}
cp += len;
}
send_pkt:
/* Send packet */
*((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
send_len = cp - xbuf;
/* NB: send_len value is preserved in code below
* for potential resend */
send_again:
#if ENABLE_DEBUG_TFTP
fprintf(stderr, "sending %u bytes\n", send_len);
for (cp = xbuf; cp < &xbuf[send_len]; cp++)
fprintf(stderr, "%02x ", (unsigned char) *cp);
fprintf(stderr, "\n");
#endif
xsendto(socketfd, xbuf, send_len, &peer_lsa->sa, peer_lsa->len);
/* Was it final ACK? then exit */
if (finished && (opcode == TFTP_ACK))
goto ret;
timeout = TFTP_NUM_RETRIES; /* re-initialize */
recv_again:
/* Receive packet */
tv.tv_sec = TFTP_TIMEOUT;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(socketfd, &rfds);
switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
unsigned from_port;
case 1:
from->len = peer_lsa->len;
memset(&from->sa, 0, peer_lsa->len);
len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
&from->sa, &from->len);
if (len < 0) {
bb_perror_msg("recvfrom");
goto ret;
}
from_port = get_nport(&from->sa);
if (port == org_port) {
/* Our first query went to port 69
* but reply will come from different one.
* Remember and use this new port */
port = from_port;
set_nport(peer_lsa, from_port);
}
if (port != from_port)
goto recv_again;
goto process_pkt;
case 0:
timeout--;
if (timeout == 0) {
bb_error_msg("last timeout");
goto ret;
}
bb_error_msg("last timeout" + 5);
goto send_again; /* resend last sent pkt */
default:
bb_perror_msg("select");
goto ret;
}
process_pkt:
/* Process recv'ed packet */
opcode = ntohs( ((uint16_t*)rbuf)[0] );
recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
#if ENABLE_DEBUG_TFTP
fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
#endif
if (opcode == TFTP_ERROR) {
static const char *const errcode_str[] = {
"",
"file not found",
"access violation",
"disk full",
"illegal TFTP operation",
"unknown transfer id",
"file already exists",
"no such user",
"bad option"
};
const char *msg = "";
if (rbuf[4] != '\0') {
msg = &rbuf[4];
rbuf[tftp_bufsize - 1] = '\0';
} else if (recv_blk < ARRAY_SIZE(errcode_str)) {
msg = errcode_str[recv_blk];
}
bb_error_msg("server error: (%u) %s", recv_blk, msg);
goto ret;
}
#if ENABLE_FEATURE_TFTP_BLOCKSIZE
if (want_option_ack) {
want_option_ack = 0;
if (opcode == TFTP_OACK) {
/* server seems to support options */
char *res;
res = tftp_option_get(&rbuf[2], len - 2, "blksize");
if (res) {
int blksize = xatoi_u(res);
if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
/* send ERROR 8 to server... */
/* htons can be impossible to use in const initializer: */
/*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
/* thus we open-code big-endian layout */
static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
xsendto(socketfd, error_8, 4, &peer_lsa->sa, peer_lsa->len);
bb_error_msg("server proposes bad blksize %d, exiting", blksize);
goto ret;
}
#if ENABLE_DEBUG_TFTP
fprintf(stderr, "using blksize %u\n",
blksize);
#endif
tftp_bufsize = blksize + 4;
/* Send ACK for OACK ("block" no: 0) */
block_nr = 0;
continue;
}
/* rfc2347:
* "An option not acknowledged by the server
* must be ignored by the client and server
* as if it were never requested." */
}
bb_error_msg("blksize is not supported by server"
" - reverting to 512");
tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4;
}
#endif
/* block_nr is already advanced to next block# we expect
* to get / block# we are about to send next time */
if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
if (recv_blk == block_nr) {
len = full_write(localfd, &rbuf[4], len - 4);
if (len < 0) {
bb_perror_msg(bb_msg_write_error);
goto ret;
}
if (len != (tftp_bufsize - 4)) {
finished = 1;
}
continue; /* send ACK */
}
if (recv_blk == (block_nr - 1)) {
/* Server lost our TFTP_ACK. Resend it */
block_nr = recv_blk;
continue;
}
}
if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
/* did server ACK our last DATA pkt? */
if (recv_blk == (uint16_t) (block_nr - 1)) {
if (finished)
goto ret;
continue; /* send next block */
}
}
/* Awww... recv'd packet is not recognized! */
goto recv_again;
/* why recv_again? - rfc1123 says:
* "The sender (i.e., the side originating the DATA packets)
* must never resend the current DATA packet on receipt
* of a duplicate ACK".
* DATA pkts are resent ONLY on timeout.
* Thus "goto send_again" will ba a bad mistake above.
* See:
* [url]http://en.'s_Apprentice_Syndrome[/url]
*/
}
ret:
if (ENABLE_FEATURE_CLEAN_UP) {
close(socketfd);
free(xbuf);
free(rbuf);
}
return finished == 0; /* returns 1 on failure */
}
int tftp_main(int argc, char **argv);
int tftp_main(int argc, char **argv)
{
len_and_sockaddr *peer_lsa;
const char *localfile = NULL;
const char *remotefile = NULL;
#if ENABLE_FEATURE_TFTP_BLOCKSIZE
const char *sblocksize = NULL;
#endif
int port;
USE_GETPUT(int cmd;)
int fd = -1;
int flags = 0;
int result;
int blocksize = TFTP_BLOCKSIZE_DEFAULT;
/* -p or -g is mandatory, and they are mutually exclusive */
opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
USE_GETPUT("?g--p:p--g");
USE_GETPUT(cmd =) getopt32(argv,
USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
"l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
&localfile, &remotefile
USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
argv += optind;
flags = O_RDONLY;
if (CMD_GET(cmd))
flags = O_WRONLY | O_CREAT | O_TRUNC;
#if ENABLE_FEATURE_TFTP_BLOCKSIZE
if (sblocksize) {
blocksize = xatoi_u(sblocksize);
if (!tftp_blocksize_check(blocksize, 0)) {
return EXIT_FAILURE;
}
}
#endif
if (!localfile)
localfile = remotefile;
if (!remotefile)
remotefile = localfile;
/* Error if filename or host is not known */
if (!remotefile || !argv[0])
bb_show_usage();
fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
if (!LONE_DASH(localfile)) {
fd = xopen(localfile, flags);
}
port = bb_lookup_port(argv[1], "udp", 69);
peer_lsa = xhost2sockaddr(argv[0], port);
#if ENABLE_DEBUG_TFTP
fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
xmalloc_sockaddr2dotted(&peer_lsa->sa),
remotefile, localfile);
#endif
result = tftp( USE_GETPUT(cmd,) peer_lsa, remotefile, fd, port, blocksize);
if (ENABLE_FEATURE_CLEAN_UP)
close(fd);
if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) {
unlink(localfile);
}
return result;
}
#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
-
教你写一个ftp协议(文件传输协议)---FTP协议的C语言实现
2019-11-29 13:18:44本文链接:... FTP协议的C语言实现 https://blog.csdn.net/Zhu_Zhu_2009/article/details/82147473 一、FTP协议简介 FTP 是File Transfer Protocol(文件传输协议)的英文简称...本文链接:https://blog.csdn.net/weixin_42898819/article/details/81502058
FTP协议的C语言实现
https://blog.csdn.net/Zhu_Zhu_2009/article/details/82147473
一、FTP协议简介
FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:”下载”(Download)和”上传”(Upload)。”下载”文件就是从远程主机拷贝文件至自己的计算机上;”上传”文件就是将文件从自己的计算机中拷贝至远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。
二、FTP Server的用户分类及权限归属
在考虑FTP服务器安全性工作的时候,第一步要考虑的就是谁可以访问FTP服务器。以下有三种客户可以访问FTP Server:
一类是Real帐户。这类用户是指在FTP服务上拥有帐号。当这类用户登录FTP服务器的时候,其默认的主目录就是其帐号命名的目录。但是,其还可以变更到其他目录中去。如系统的主目录等等。
第二类帐户是Guest用户。在FTP服务器中,我们往往会给不同的部门或者某个特定的用户设置一个帐户。但是,这个账户有个特点,就是其只能够访问自己的主目录。服务器通过这种方式来保障FTP服务上其他文件的安全性。拥有这类用户的帐户,只能够访问其主目录下的目录,而不得访问主目录以外的文件。
第三类帐户是Anonymous(匿名)用户,这也是我们通常所说的匿名访问。这类用户是指在FTP服务器中没有指定帐户,但是其仍然可以进行匿名访问某些公开的资源。三、写一个简单的ftp协议
下面我们写一个简单的匿名用户可以访问的ftp协议,前两种账户的功能我会在后续博客中逐一完善。在写一个ftp协议之前你需要了解以下几点:
- ftp作为一个服务器端和客户端相互传输文件的协议,我们在写的时候就要分别写服务器端程序server_ftp.py和客户端程序client_ftp.py。
-
我们要实现多个客户端可以同时访问服务器端,所以要通过多线程的方式来使得多个客户端访问,在这里我们服务器端通过socketserver来写。
-
最后通过将一个txt文件通过客户端上传到服务器端来验证所写代码的准确性。在下面的代码中我会详细备注每条语句完成的功能。
客户端代码:
import socket #导入socket模块,用来实现socket通信 import os #导入os模块,主要用来调用系统命令,获得路径 import json #导入json模块,将字符串形式的json数据转化为字典,也可以将Python中的字典数据转化为字符串形式的json数据 class FtpClient(object ): def __init__(self): self.client=socket.socket() #声明客户端利用socket通信 def help(self): #写一个打印一些指令的帮助信息函数, msg=''' ls pwd cd../.. get filename put filename ''' def connect(self,ip,port): #定义一个连接服务器函数,调用client.connect()方法,连接服务器端 self.client.connect((ip,port)) def interactive(self): #定义一个与服务器交互的函数 while True: cmd=input('>>').strip() #用户在客户端输入指令,strip()去掉用户输入指令的空格和换行符 if len(cmd)==0:continue cmd_str=cmd.split()[0] #拆分指令的第一个字符赋给cmd_str,永远都是指令help() #反射 if hasattr(self,'cmd_%s'%cmd_str ): #hasattr() 函数用于判断对象是否包含对应的属性 func=getattr(self,'cmd_%s'%cmd_str) # 函数用于返回一个对象属性值 func(cmd) #取到help中的指令 ls,pwd,get ,put。然后传入后面的 filename,这样后面的函数名字就叫做cmd_put cmd_get... else: self.help() def cmd_put(self,*args): #写一个通过客户端上传文件的函数,*args为了接收更多数据 #上传一个文件 cmd_split= args[0].split() #将传入的第一个参数赋值给cmd_split,变为列表 if len(cmd_split)>1: #这里大于1 因为最后我们输入put filename.txt进行验证,由于存在put所以大于1 filename=cmd_split[1] if os.path.isfile(filename): #判断要上传的文件是否存在 filesize=os.stat(filename).st_size #获取文件大小 #发送文件大小,文件名,进行的操作(这里我们默认put上传数据)给服务器,所以写成json字典形式,需要扩展直接在这加 msg_dic={ 'action':'put', 'filename':filename, 'size':filesize } self.client.send(json.dumps(msg_dic).encode('utf-8'))#发给服务器端,json.dumps()字典转换为json格式 server_response=self.client.recv(1024) #等待服务器响应 f=open(filename ,'rb') #打开文件,发送给服务器 for i in f: self.client.send(i) else: print('文件传输完毕') f.close() else: print(filename ,'is not exist') def cmd_get(self): #定义一个从服务器下载文件函数,这里和上面大同小异,先不写 pass ftp=FtpClient() #实例化 ftp.connect('localhost',9999) #连接服务器端口 ftp.interactive() #调用和服务器交互函数
服务器端代码
import socketserver #利用socketserver来写 import json ,os #自己写一个请求处理类,继承BaseRequestHandler class MyTCPHandler(socketserver.BaseRequestHandler): def put(self,*args): #接收客户端文件 cmd_dic=args[0] filename=cmd_dic['filename'] #获取文件名 filesize=cmd_dic['size'] #获取文件大小 if os.path.isfile(filename ): #如果已经存在上传文件,新建一个文件 f=open(filename+'.new','wb') else: #如果不存在 给客户端响应上传 f=open(filename ,'wb') self.request.send(b'200 ok') #响应客户端 received_size=0 while received_size < filesize : #循环接收文件 data=self.request.recv(1024) f.write(data) received_size +=len(data) else: print('file[%s] has overload..'%filename ) #文件传输完成 #跟客户端的交互在handle中 def handle(self): while True : try: self.data=self.request.recv(1024).strip() #format格式化 打印客户端ip地址 print('{}wrote:'.format(self.client_address[0])) print(self.data) cmd_dic=json.loads(self.data.decode())#json字符串转为字典 action=cmd_dic['action'] #获取进行的操作 这里默认是put #反射 if hasattr(self,action ): #判断put操作是否存在 func=getattr(self ,action ) func(cmd_dic) except ConnectionResetError as e: print(e) break if __name__ =='__main__': HOST,POST='localhost',9999 # 实例化TCPServer server=socketserver.ThreadingTCPServer((HOST,POST),MyTCPHandler ) #ThreadingTCPServer:多线程,多个客户端可以请求 #这样此服务器就可以让多个客户端连接 #处理多个请求 server.serve_forever()
运行结果
我们把需要测试的文件lianxiren.txt放在客户端目录下,先运行服务器,在运行客户端,并且输入put lianxiren.txt 可以发现文件传输完成,在服务器的文件夹中会发现传输过去的txt文件。
-
FTP客户端程序,c语言编写
2010-04-24 12:56:21基于ftp协议编写的ftp客户端,支持多线程下载,断点下载等功能 -
C语言实现基于winsock的ftp客户端程序
2012-11-26 12:36:21用c语言编写的,Winsock实现FTP客户端,实现断点上传和下载,支持pasv和port模式,列出服务器目录内容,改变服务器目录,添加删除目录,删除文件,断开连接。 平台:win7 开发工具:VS2008 -
C语言实现FTP客户端(已编译,亲试可用~)
2020-12-07 13:36:06C语言实现简易FTP客户端 1、搭建FTP服务器 此处用到的FTP服务器是在Win10系统下搭建的,具体服务器搭建与配置过程可参照另一篇我...FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器C语言实现被动模式下,FTP客户端从FTP服务器下载文件
-
1、搭建FTP服务器
- 此处用到的FTP服务器是在Win10系统下搭建的,具体服务器搭建与配置过程可参照另一篇我写的文章《Win10搭建FTP服务器详细教程》,本文中用到的FTP命令里的传递参数举例皆与上面提到的FTP服务器配置匹配。
- 此处用到的FTP服务器是在Win10系统下搭建的,具体服务器搭建与配置过程可参照另一篇我写的文章《Win10搭建FTP服务器详细教程》,本文中用到的FTP命令里的传递参数举例皆与上面提到的FTP服务器配置匹配。
-
2、名词解释
-
1、FTP协议
- FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。
-
2、被动模式(Passive)
- FTP服务器收到PASV命令后,随机打开一个高端端口P(端口号大于1024)并且通知客户端在这个端口上传送数据的请求,客户端连接FTP服务器此端口,通过三次握手建立通道,然后FTP服务器将通过这个端口进行数据的传送。
-
3、命令通道(Command Socket)
- 客户端打开一个本地端口N(N > 1024)用来连接FTP服务器的 21 端口,建立命令通道,它负责 FTP 命令的发送和接收服务器返回的响应信息。
-
4、数据通道(Data Socket)
- 对于有数据传输的操作,比如传输下载文件,我们需要建立另一个数据通道来完成。进入被动模式后,服务器会打开一个端口P(P > 1024),此时客户端会打开一个新的端口(N + 1)去连接服务器的端口P,此通道负责传输文件数据。
- 对于有数据传输的操作,比如传输下载文件,我们需要建立另一个数据通道来完成。进入被动模式后,服务器会打开一个端口P(P > 1024),此时客户端会打开一个新的端口(N + 1)去连接服务器的端口P,此通道负责传输文件数据。
-
-
3、主要用到的FTP命令和对应的FTP响应码
FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令都以 "\r\n"结束。-
- USER:“USER xiaokeai\r\n” → 用户名为:xiaokeai
- PASS:“PASS 123456\r\n” → 密码为:123456
- TYPE:“TYPE I\r\n” → 设置传输方式为二进制模式:I
- SIZE:“SIZE /AC1/APP.bin\r\n” → 获取APP.bin这文件的大小,文件路径:/AC1/APP.bin
- PASV:"PASV\r\n " → 让服务器进入被动模式
- RETR:“RETR /AC1/APP.bin \r\n” → 下载命令,文件路径:/AC1/APP.bin
- QUIT:“QUIT\r\n” → 退出FTP服务器命令
-
2、FTP响应码
-
- 220:“220 Microsoft FTP Service” → 命令通道已连接,服务器发送欢迎信息
- 331:”331 Password required” → 用户名正确,请求密码
- 230:”230 User logged in.” → 用户已登录
- 200:”200 Tpye set to I.” → 文件传输模式已改为二进制模式
- 213:”213 7968” → APP.bin文件大小为:7968个字节
- 227:”227 Entering Passive Mode (192,168,1,110,19,190).” → 服务器IP:192.168.1.110
服务器数据端口号:19 * 256 + 190 = 5054 - 125:”125 Data connection already open; Transfer starting.” → 数据连接已建立,开始传输数据
- 226:”226 Transfer complete.” → 数据传输完毕,提示客户端断开数据连接
- 221:”221 Transfer complete.” → 提示客户端断开命令连接
-
-
4、Socket 编程的几个重要步骤
- socket( ); → 服务器/客户端打开一个端口
- connect( ); → 创建一个命令/数据通道连接
- listen( ); → 监听
- close( ); → 断开命令/数据通道连接
-
5、实现被动模式下,FTP客户端从FTP服务器下载文件
-
1、客户端和服务器建立命令通道连接
- 客户端端口N和服务器端口21建立命令通道连接后,服务器返回“220…”欢迎信息
- 客户端端口N和服务器端口21建立命令通道连接后,服务器返回“220…”欢迎信息
-
2、客户端登陆FTP服务器
- 客户端发送用户名和密码,服务器验证通过后,会返回230的响应码。然后客户端就可以向服务器端发送命令了。
- 客户端发送用户名和密码,服务器验证通过后,会返回230的响应码。然后客户端就可以向服务器端发送命令了。
-
3、客户端设置文件传输方式,获取文件信息
- 客户端发送TYPE命令设置文件传输方式为二进制,再发送SIZE命令获取待下载文件的大小(单位字节)
- 客户端发送TYPE命令设置文件传输方式为二进制,再发送SIZE命令获取待下载文件的大小(单位字节)
-
4、客户端让FTP服务器进入被动模式,建立数据通道连接
- 客户端在下载文件前,要先发送PASV命令让服务器进入被动模式。进入被动模式后,服务器会打开数据端口P > 1024并监听,并返回响应码 227,227响应信息里包含服务器IP和端口号P的信息,客户端打开端口N + 1,与服务器端口P建立数据通道连接。
- 客户端在下载文件前,要先发送PASV命令让服务器进入被动模式。进入被动模式后,服务器会打开数据端口P > 1024并监听,并返回响应码 227,227响应信息里包含服务器IP和端口号P的信息,客户端打开端口N + 1,与服务器端口P建立数据通道连接。
-
5、客户端通过被动模式下载文件
- 建立数据通道连接后,客户端向服务器发送RETR命令下载文件,服务器会返回125响应码,并开始通过数据通道向客户端发送文件内容。
- 建立数据通道连接后,客户端向服务器发送RETR命令下载文件,服务器会返回125响应码,并开始通过数据通道向客户端发送文件内容。
-
6、下载完成,客户端退出服务器
- 当客户端下载完成后,服务器会发226响应码,客户端断开数据通道连接,然后服务器会发221响应码,客户端再断开命令通道连接。
- 当客户端下载完成后,服务器会发226响应码,客户端断开数据通道连接,然后服务器会发221响应码,客户端再断开命令通道连接。
-
-
6、部分代码展示
-
213响应解析函数,获取下载文件大小
//举例:”213 7968” uint8 Get_213_FileSize(char *arg) { uint16 n; char* tok = 0; tok = strtok(arg, " "); tok = strtok(NULL, "\r\n"); File_Size = atoi32(tok, 10); printf(" File_Size is : %d byte\n", File_Size); return 1; }
-
227响应解析函数,获取服务器 IP 和端口号 P
//227 Entering passive mode (h1,h2,h3,h4,p1,p2) uint8 Get_227_IpPort(char *arg) { int i; int j = 0; char* tok = 0; strtok(arg, "("); //去掉括号以前的字符串,留下h1,h2,h3,h4,p1,p2) //获取 IP for(i = 0; i < 4; i++) { if(i == 0) { tok = strtok(NULL, ",\r\n"); //取 h1 } else { tok = strtok(NULL, ","); //分别取 h2, h3, h4 } ftp_data_ip[i] = (uint8_t)atoi16(tok, 10); if(!tok) { printf(" bad ipport : %s\r\n", arg); return 0; } } //获取端口号 port j = 0; ftp_data_port = 0; for (i = 0; i < 2; i++) { if(j == 0) { tok = strtok(NULL, ","); ftp_data_port = (atoi16(tok, 10) << 8); j++; } else { tok = strtok(NULL, ","); tok = strtok(tok, ")"); ftp_data_port += (uint16_t)(atoi16(tok, 10) & 0xff); } if(!tok) { printf(" bad ipport : %s\r\n", arg); return 0; } } printf(" ip : %d.%d.%d.%d, port : %d\r\n", ftp_data_ip[0], ftp_data_ip[1], ftp_data_ip[2], ftp_data_ip[3], ftp_data_port); return 1; }
-
服务器响应数据的接收函数
void proc_ftpconrol(char * buf) { uint16_t Responses; uint8_t dat[30]={0,}; memset(dat,0,30); Responses = (buf[0] - '0') * 100 + (buf[1] - '0') * 10 + (buf[2] - '0'); printf(" Responses : %d\r\n", Responses); switch(Responses) { case 220: //连上服务器之后,接收到服务器发送的220 欢迎文字 printf("\r\n Input your User ID > "); sprintf((char*)dat, "USER %s\r\n", "xiaokeai"); printf("\r\n"); send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat)); //用USER命令发送用户名 break; case 331: //服务器接收到用户名,返回331状态 printf("\r\n Input your Password > "); sprintf((char*)dat, "PASS %s\r\n", "123456"); printf("\r\n"); send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat)); //用PASS命令发送登录密码 break; case 230: //服务器接收到密码后,返回230表示密码正确,返回530表示密码错误 printf("\r\n User logged in, proceed > "); sprintf((char*)dat, "TYPE %c\r\n", TransferBinary); printf("\r\n"); send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat)); //登陆成功后,再发送一条TYPE I命令,进入二进制模式,这样获取的文件数据的时候,就会以二进制字节流发送,避免以ASCII格式发送文件数据 break; case 200: //服务器返回200 //如果服务器还没进入被动模式,此时表示设置二进制模式成功 printf("\n Get File Size..."); sprintf((char*)dat, "SIZE %s\r\n", "/AC1/APP.bin"); printf("\r\n"); send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat)); //发送SIZE命令,获取下载文件的大小 break; case 213: //接收SIZE命令,服务器返回 213 FileSize (文件大小单位为字节,10进制数) Get_213_FileSize(buf); printf("\r\n Set Server To Passive Mode..."); sprintf((char*)dat, "PASV\r\n"); printf("\r\n"); send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat)); //发送PASV命令,设置服务器进入被动模式 break; case 227: //接收PPASV命令,服务器返回 227 Entering passive mode (h1,h2,h3,h4,p1,p2) if(Get_227_IpPort(buf) == 0) { printf(" Bad port syntax\r\n"); } else { printf(" Go Set Up Data Sock...\r\n "); //服务器已进入被动模式,准备建立 Data Socket 数据传输通道 } break; case 125: //建立数据通道连接后,服务器返回“125 Data connection already open Transfer starting” printf(" Entering Download Mode...\r\n\n"); FlashAddrCur = FLASH_APP2_ADDR; RecDataLenCur = 0; break; case 226: //服务器发送完升级包后,主动发送226,服务器那边断开数据连接通道了 close(SOCK_FTP_DATA); //客户端关闭数据传输socket printf(" Client Close Data Socket...\r\n"); printf("\r\n Quit of the Server...\r\n"); sprintf((char*)dat, "QUIT\r\n"); send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((const char*)dat)); //发送QUIT命令,请求退出服务器 break; case 221: //服务器接收到QUIT命令,返回的221 printf(" Client Close Control Socket...\r\n"); close(SOCK_FTP_CTRL); //客户端关闭命令传输socket break; default: printf("\r\n Default Status = %d\r\n",(uint16_t)Responses); break; } }
-
-
7、小结
欢迎纠正,(づ ̄3 ̄)づ╭❤~
如果有帮助到你,能点个赞吗?ღ( ´・ᴗ・` ) 比心 -
-
FTP客户端和服务器源代码(C语言)实现
2021-05-20 19:25:32课程设计:FTP的设计与实现/****************client.c****************/#include #include #include #pragma comment(lib,"ws2_32.lib")#define DEFAULT_PORT 2302#define DEFAULT_BUFFER 2048#define DEFAULT_MESS.... -
FTP/TFTP协议的C语言实现
2019-07-14 19:28:07用C语言实现FTP协议客户端的主要功能 开源一款ftp软件——filezilla SFTP和FTP 在Linux环境下,一般通过第三方的SSH工具连接到Linux,当需要传输文件到Linux服务器当中,很多人习惯用ft... -
ftp客户端ftpclient纯C语言winsock实现socket编程
2009-07-13 18:50:25ftp客户端ftpclient纯C语言winsock实现socket编程 /* *本程序是2009年计算机网络课程设计作品。 *本程序参考rfc959标准。能准确与遵守此标准的服务器进行信息交互。 *本人不保留任何版权。 *本程序仅供学习研究测试... -
C语言实现的简易FTP客户端
2019-04-19 10:03:10FTP协议使用字符串格式命令字,每条命令都是一行字符串,以“\r\n”结尾。 客户端发送格式是:命令+空格+参数+"\r\n"的格式 服务器返回格式是以:状态码+空格+提示字符串+"\r\n"的格式,代码只要解析状态码就可以... -
实现一个FTP服务系统 c语言
2020-12-13 15:05:36基于客户/服务器交互模式,采用TCP/IP通信协议,实现一个支持远程文件传输的客户/服务器软件,支持文件的上传和下载。其中客户端软件命名为myftp,服务器软件命名为ftpserver,具体功能需求如下: 服务器 启动方式... -
基于C语言实现的一个ftp_client demo
2021-06-20 22:24:09一 .FTP协议 FTP全称:文件传输协议(File Transfer Protocol), 不同于普通的C/S通信方式,ftp协议是建立两个链路去传输数据和控制消息。 有两种工作模式:主动连接模式(PORT)和被动连接模式(PASV),这两种工作方式的... -
LINUX学习-用C语言实现FTP项目
2019-08-18 17:55:25FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。在开发网站的时候,通常利用FTP协议把网页或程序传到Web... -
C语言编写Web服务器
2021-05-06 21:37:24C语言编写Web服务器端口Tcp服务器代码实现 端口 什么是端口? 物理端口:电脑网口、USB、看的见的接口。 虚拟端口:程序和网络进行通信的端口。 端口就好比一个房子的门,是初入这个房子的必经之路。 端口号 ... -
ftp客户端主动模式代码(c语言)
2015-06-02 09:38:08跪求大神指点ftp主动模式如何用c编写,知道客户端和服务器端传输流程,但是写不出代码 -
用C语言实现基于TCP协议的网络通讯
2021-05-21 11:32:53《用C语言实现基于TCP协议的网络通讯》由会员分享,可在线阅读,更多相关《用C语言实现基于TCP协议的网络通讯(6页珍藏版)》请在人人文库网上搜索。1、用C#实现基于TCP协议的网络通讯TCP协议是一个基本的网络协议,... -
使用C和Socket 通信实现 FTP 客户端程序
2021-05-21 04:05:38下面是我更正之后的内容:文件传输协议(FTP)作为网络共享文件的传输协议,在网络应用软件中具有广泛的应用。FTP的目标是提高文件的共享性和可靠高效地传送数据。在传输文件时,FTP 客户端程序先与服务器建立连接,... -
Linux C 实现简单FTP
2013-11-22 16:04:06Linux C语言 实现简单FTP服务器与客户端 -
Linux下C语言编写基于命令行的FTP客户端与服务端
2012-12-07 14:14:56这是--Linux程序设计--...基于客户/服务器交互模式,采用TCP/IP通信协议,实现以支持远程文件传输的客户/服务器软件,支持文件的上传和下载。其中客户端软件命名为myftp,服务器软件命名为ftpserver 服务器 启动方式 -
FTP协议(QT实现C语言抓包、搭建Filezilla服务器、Wireshark抓包)
2018-12-13 16:45:54目录 ...(五)、QT实现C语言捕获FTP包 (六)、Wireshark抓FTP流量包分析 (附录)FTP的C语言代码实现 内容 1、FTP原理 2、FTP工作方式 3、FTP传输模式 4、iis搭建ftp服务器(fil... -
基于C语言的FTP服务器程序设计网络信息安全实验.docx
2021-05-24 06:57:42基于C语言的FTP服务器程序设计网络信息安全实验网络与信息安全实验PAGE \* MERGEFORMAT- 2 -网络与信息安全实验PAGE \* MERGEFORMAT- 1 -基于C++的FTP服务器程序设计摘要: 本实验的目的是设计一个FTP服务器系统,... -
Linux下使用UDP协议传输数据(示例)
2019-01-02 21:32:33该示例演示了如何使用UDP协议传输一段数据,以及演示了 UDP的不保证正确性.最好将发送端和接收端布置到不同电脑上(经过互联网最佳),以演示丢包的可能性.在本机环路(127.1.1.1)上测试时,发送1001个包,收到1001个... -
C语言实现url解析小实例
2022-02-17 21:30:08前言 前面一口君写了一篇关于url的文章: 《一文带你理解URI 和 URL 有什么区别?...本篇在此基础上,编写一个简单的用于解析url的小例子, 最终目标是解析出URL中所有的数据信息。 程序流程图 ... -
FTP的简单使用(一、利用FTPlib写一个客户端)
2021-06-13 22:00:27FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。在开发网站的时候,通常利用FTP协议把网页或程序传到Web... -
FTP主动与被动传输引发的协议那些事
2016-07-26 22:04:141.什么是FTP协议; FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP... -
linux下的C语言开发
2019-03-06 10:32:01作为一个真正的程序员来说,如果没有在linux下面用C语言编写过完整的程序,那么只能说他对C语言本身的理解还相关肤浅,对系统本身的认识也不够到位。作为程序员来说,linux系统为我们提供了很多理想的环境,这其中包... -
C语言命令行编程
2020-02-14 18:01:31C黑窗口程序可以调用其他任何C语言黑窗口程序在命令行执行, 调用方法就是 system( "C:\\CMD.exe" ); 调用system函数,传入路径 我们通过CMD,也可以调用其他任何C语言黑窗口程序 7. 通过... -
单片机TCP IP协议栈实现的原理(2)
2021-05-25 06:08:28已有的单片机TCP IP协议栈由于TCP协议栈的程序流程较为复杂,因为程序...一个稳定的协议栈需要在实际应用中经过长时间的测试,所以编写一个稳定的单片机TCP IP协议栈更加困难。为此另外一种选择就是使用现有的TCP IP协... -
win7+iis+cgi+c语言
2021-05-23 05:45:55自己的编程爱好也是从那里一点一点被满足。...对于cgi,不算接触,但是还是听说过之前,对于纯粹写普通网站的人来说CGI却是很古老的东西了,之前在大学的时候学校的ftp搜索引擎,是一位特别牛b的学长写的,用的就是... -
C语言预习
2021-05-20 07:02:39一组指示计算机或其他具有信息处理能力装置执行动作或做出判断的指令,通常用某种程序设计语言编写,运行于某种目标体系结构上。1.2什么是编程语言?用来定义计算机程序的形式语言(它是一种被标准化的交流技巧,用来...