数据连接
- 主动模式
主动模式下连接的示意图:
客户端将自己的ip和port发送给服务器,等待服务器的主动连接。
主动模式下,解析客户端发来的ip和port示意图:
- 被动模式
被动模式的情况下,服务器建立TCP连接,客户端来连接服务器,服务器需要获取自己的ip和port,并发送给客户端。 - 主动模式下工作的过程
leapftp 客户端显示的是图形界面,但是实际上服务器发送过来的是文本。所以我们只需要把目录的文本发送给客户端即可。
核心:怎么获取用户的目录
结构体stat:
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blksize_t st_blksize; /* Block size for filesystem I/O */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
结构体 dirent
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
const char* statbuf_get_perms(struct stat *sbuf)
{
static char perms[] = "----------";
perms[0] = '?';
mode_t mode = sbuf->st_mode;
switch (mode & S_IFMT) {
case S_IFREG:
perms[0] = '-';
break;
case S_IFDIR:
perms[0] = 'd';
break;
case S_IFLNK:
perms[0] = 'l';
break;
case S_IFIFO:
perms[0] = 'p';
break;
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFBLK:
perms[0] = 'b';
break;
}
if (mode & S_IRUSR) {
perms[1] = 'r';
}
if (mode & S_IWUSR) {
perms[2] = 'w';
}
if (mode & S_IXUSR) {
perms[3] = 'x';
}
if (mode & S_IRGRP) {
perms[4] = 'r';
}
if (mode & S_IWGRP) {
perms[5] = 'w';
}
if (mode & S_IXGRP) {
perms[6] = 'x';
}
if (mode & S_IROTH) {
perms[7] = 'r';
}
if (mode & S_IWOTH) {
perms[8] = 'w';
}
if (mode & S_IXOTH) {
perms[9] = 'x';
}
if (mode & S_ISUID) {
perms[3] = (perms[3] == 'x') ? 's' : 'S';
}
if (mode & S_ISGID) {
perms[6] = (perms[6] == 'x') ? 's' : 'S';
}
if (mode & S_ISVTX) {
perms[9] = (perms[9] == 'x') ? 't' : 'T';
}
return perms;
}
const char* statbuf_get_date(struct stat *sbuf)
{
static char datebuf[64] = {0};
const char *p_date_format = "%b %e %H:%M";//月 日 时 分
struct timeval tv;//存储当前时间的结构体
gettimeofday(&tv, NULL);//使用函数gettimeofday()函数来得到时间。它的精度可以达到微妙,第二个参数带回的是时区的信息,NULL默认是系统的时区
time_t local_time = tv.tv_sec;
if (sbuf->st_mtime > local_time || (local_time - sbuf->st_mtime) > 60*60*24*182)//早于系统时间半年前的时间格式
{
p_date_format = "%b %e %Y";//月 日 年
}
//这一行做了修改!!!
struct tm* p_tm = localtime(&sbuf->st_mtime);//根据文件创建总秒数得到标准时间
strftime(datebuf, sizeof(datebuf), p_date_format, p_tm);
return datebuf;
}
void list_common(session_t *sess)
{
DIR *dir = opendir(".");
if (dir == NULL)
{
return;
}
struct dirent *dt;
struct stat sbuf;//stat结构体里面存的是读到的文件的信息
while ((dt = readdir(dir)) != NULL)
{
if (lstat(dt->d_name, &sbuf) < 0)//获取链接文件的信息(不穿透)
{
continue;
}
if (dt->d_name[0] == '.') //判断隐藏文件的
{
continue;
}
char buf[1024] = {0};
const char *perms = statbuf_get_perms(&sbuf);//sysutil中实现的
int off = 0;
//sprintf()函数的返回值:写入到 buffer 的字符数(不计空终止字符),或若输出错误或编码错误(对于字符串和字符转换指定符)发生则为负值。
off += sprintf(buf, "%s ", perms);
off += sprintf(buf + off, " %3d %-8d %-8d ", (int)sbuf.st_nlink, sbuf.st_uid, sbuf.st_gid);
off += sprintf(buf + off, "%8lu ", (unsigned long)sbuf.st_size);
const char *datebuf = statbuf_get_date(&sbuf);//sysutil中实现的
off += sprintf(buf + off, "%s ", datebuf);
if (S_ISLNK(sbuf.st_mode))//通过st_mode判断是不是链接文件
{
char tmp[1024] = {0};
/*readlink()将符号链接路径名的内容放在
缓冲区buf,大小为bufsiz。 readlink()不附加null
字节到buf。它将(默默地)截断内容(达到一定长度)
的bufsiz字符),以防缓冲区太小而无法容纳所有
内容。成功执行后,这些调用将返回放置在buf中的字节数。
(如果返回的值等于bufsiz,则截断可能包含
错误发生时,返回-1并将errno设置为指示错误。
*/
readlink(dt->d_name, tmp, sizeof(tmp));
off += sprintf(buf + off, "%s -> %s\r\n", dt->d_name, tmp);
}
else
{
off += sprintf(buf + off, "%s\r\n", dt->d_name);
}
writen(sess->data_fd, buf, strlen(buf));
}
closedir(dir);
}
4 226 Directory send OK.
linux下时间函数的关系:
获取用户目录中,文件时间的处理: