2.重要套接口函数
Socket函数:
#include<sys/socket.h>
int socket(int family,int type,int protocol);
返回非负描述字则成功,-1出错。
family指明协议族(IPv4、IPv6或Unix),type表示套接口的类型,protocol一般设置为0。
Connect函数:
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr * servaddr, socklen_t addrlen);
返回0成功,-1出错。
Sockfd是由socket函数返回的套接口描述字,第二、第三个参数分别是一个指向套接口地址结构的指针和该结构的大小。地址结构必须含有服务器的IP地址和端口号。
Bind函数:
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr * myaddr, socklen_t addrlen);
返回0成功,-1出错。
第二个参数是一个指向特定协议的地址结构的指针,第三个参数是该地址结构的长度。这个函数给套接口分配一个协议地址,协议地址的含义取决于协议本身。
Listen函数:
#include<sys/socket.h>
int listen(int sockfd,int bavklog);
返回0成功,-1出错。
第二个参数规定了内核为此套接口排队的最大连接个数。函数listen只被服务器调用,将未连接的套接口转换成被动套接口,指示内核应接受指向此套接口的连接请求。调用此函数可使套接口从CLOSED状态转换到LISTEN状态。
Accept函数:
#include<sys/socket.h>
int accept(int sockfd,const struct sockaddr * cliaddr, socklen_t addrlen);
返回非负描述字成功,-1出错。
此函数由服务器调用,从已完成连接队列头返回下一个已完成连接。若已完成连接队列为空,则进程睡眠。参数cliaddr和addrlen用来返回连接对方进程(客户)的协议地址。
Close函数:
#include<unistd.h>
int close(int sockfd);
套接口的close缺省功能是将套接口做上“已关闭”标记,并立即返回到进程。这个套接口描述字不能再为进程所用。
Shutdown函数:
#include<sys/socket.h>
int shutdown(int s,int how);
成功返回0,否则返回-1。
该函数调用将导致socket描述符s所指的全双工socket连接被部分或全部关闭。
Getsockopt和setsockopt函数;
#include<sys/socket.h>
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen);
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t *optlen);
sockfd必须指向一个打开的套接字,level指定系统中解释选项的代码:普通套接口代码或特定于协议的代码;optval是一个指向变量的指针。
Recv和send函数:
#include<sys/socket.h>
ssize_t rect(int sockfd,void * buff,size_t nbytes,int flags);
ssize_t send(int sockfd,const void * buff,size_t nbytes,int flags);
成功则返回读入或写出的字节数,出错返回-1。
这两个函数和标准的read和write函数很类似,只是多了一个附加的参数flags。
3.文件I/O与高级文件操作函数
open函数:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int open(const char *pathname,int flags,…mode_t mode);
打开(或创建)文件失败返回-1。
第一个参数pathname是要打开(或创建)的文件名或含路径的文件名。第二个参数是标志打开的方式,这个参数用来说明这个系统调用的多个选择项。
creat函数:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
int creat(const char *pathname, mode_t mode);
创建文件失败返回-1。
参数含义与open函数相同。
read函数:
#include<unist.h>
size_t read(int filedes,void *buff,size_t nbytes);
调用成功返回读取的字节数,返回0表示文件指针在文件尾。错误返回-1。
该函数从文件描述符fd所指的文件中读取nbytes字节到buff所指向的内存缓冲中。
write函数:
#include<unist.h>
size_t write(int filedes,void *buff,size_t nbytes);
调用成功返回写入的字节数,返回0表示没有数据要写。错误返回-1。
该函数用来将内存中的数据写入已打开的文件中。
opendir/readdir/closedir函数:
#include<sys/types.h>
#include<dirent.h>
DIR * opendir(const char *name);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
opendir函数用来打开一个目录项,readdir读取目录项中的内容,closedir关闭一个已经打开的目录项。
chmod函数:
#include<sys/types.h>
#include<sys/stat.h>
int chmod(const char *path, mode_t mode);
调用成功返回0,错误返回-1。
该函数用于修改任何类型的一个现存文件的存取许可权。
mkdir/rmdir函数:
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
#include<unistd.h>
int mkdir(const char *pathname, mode_t mode);
int mkdir(const char *pathname);
调用成功返回0,错误返回-1。
mkdir函数用来创建一个新的目录,rmdir删除一个已有的空目录。
unlink函数:
#include<unistd.h>
int unlink(const char *pathname);
调用成功返回0,错误返回-1。
该函数用于删除一个目录项或文件。
chdir函数:
#include<unistd.h>
int chdir(const char *path);
调用成功返回0,错误返回-1。
该函数用于改变当前工作目录。
二、客户端功能实现与关键函数
1.连接到服务器
函数get_user()和get_pass()得到用户输入的用户名和密码。ftp_login()函数用输入字符与缺省用户名密码、匿名用户密码相比较,如果一致则登陆成功,否则失败。
void get_user()//得到输入用户名
{
char read_buf[64];
printf("User(Press for anonymous): ");
fgets(read_buf, sizeof(read_buf), stdin);
if(read_buf[0]=='/n')
strncpy(user, "anonymous", 9);
else
strncpy(user, read_buf, strlen(read_buf)-1);
}
void get_pass()//得到输入密码
{
char read_buf[64];
printf("Password(Press for anonymous): ");
echo_off();//隐藏密码
fgets(read_buf, sizeof(read_buf), stdin);
if(read_buf[0]=='/n')
strncpy(passwd, "anonymous", 9);
else
strncpy(passwd, read_buf, strlen(read_buf)-1);
echo_on();//取消隐藏
printf("/n");
}
int ftp_login()//登陆函数
{
int err;
get_user();
if(ftp_send_cmd("USER ", user, sock_control) < 0)
cmd_err_exit("Can not send message",1);;
err = ftp_get_reply(sock_control);//得到服务器返回
if(err == 331)
{
get_pass();
if(ftp_send_cmd("PASS ", passwd, sock_control) <= 0)
cmd_err_exit("Can not send message",1);
else
err = ftp_get_reply(sock_control);
if(err == 230)//缺省用户
{
return 1;
}
else if(err == 531)//匿名用户
{
return 2;
}
else
{
printf("Password error!/n");
return 0;
}
}
else
{
printf("User error!/n");
return 0;
}
}
函数int xconnect(struct sockaddr_in *s_addr, int type)连接到服务器。
int xconnect(struct sockaddr_in *s_addr, int type)
{
struct timeval outtime;
int set;
int s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0)
cmd_err_exit("creat socket error!", 249);
set = setsockopt(s, SOL_SOCKET,SO_RCVTIMEO, &outtime,sizeof(outtime));
if(set !=0)
{
printf("set socket %s errno:%d/n",strerror(errno),errno);
cmd_err_exit("set socket", 1);
}
//connect to the server
if (connect(s,(struct sockaddr *)s_addr,sizeof(struct sockaddr_in)) < 0)
{
printf("Can't connect to server %s, port %d/n",/
inet_ntoa(s_addr->sin_addr),ntohs(ftp_server.sin_port));
exit(252);
}
return s;
}
fgets(read_buf, sizeof(read_buf), stdin);
得到用户名和密码的关键函数。读入数据。
int s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0)
cmd_err_exit("creat socket error!", 249);
创建套接字,如创建成功,socket函数返回非负值。
//connect to the server
if (connect(s,(struct sockaddr *)s_addr,sizeof(struct sockaddr_in)) < 0)
{
printf("Can't connect to server %s, port %d/n",/
inet_ntoa(s_addr->sin_addr),ntohs(ftp_server.sin_port));
exit(252);
}
连接到服务器,如果连接成功,connect函数返回0,如果不成功,返回-1。
int ftp_send_cmd(const char *s1, const char *s2, int sock_fd)
向服务器发送客户端的命令的函数。
int ftp_send_cmd(const char *s1, const char *s2, int sock_fd)
{
char send_buf[256];
int send_err, len;
if(s1)
{
strcpy(send_buf,s1);
if(s2)
{
strcat(send_buf, s2);
strcat(send_buf,"/r/n");
len = strlen(send_buf);
send_err = send(sock_fd, send_buf, len, 0);
}
else
{
strcat(send_buf,"/r/n");
len = strlen(send_buf);
send_err = send(sock_fd, send_buf, len, 0);
}
}
if(send_err < 0)
printf("send() error!/n");
return send_err;
}
接收服务器返回信息函数
//get the server's reply message from sock_fd
int ftp_get_reply(int sock_fd)
{
static int reply_code = 0,count=0;
char rcv_buf[512];
count=read(sock_fd, rcv_buf, 510);
if(count > 0)
reply_code = atoi(rcv_buf);
else
return 0;
while(1)
{
if(count <= 0)
break;
rcv_buf[count]='/0';
printf("%s",rcv_buf);
count=read(sock_fd, rcv_buf, 510);
}
return reply_code;
}
send_err = send(sock_fd, send_buf, len, 0);
send函数发送成功返回字节数大于0,小于0则出错。
int xconnect_ftpdata()
连接服务器数据流函数。
get_sock = socket(AF_INET, SOCK_STREAM, 0);
set = setsockopt(get_sock, SOL_SOCKET,SO_RCVTIMEO, /
&outtime,sizeof(outtime));
set = setsockopt(get_sock, SOL_SOCKET,SO_REUSEADDR, /
&opt,sizeof(opt));
新建套接字传输数据流。
set = bind(get_sock, (struct sockaddr *)&local_host, /
sizeof(local_host));
if(set != 0 && errno == 11)
{
client_port = rand_local_port();
continue;
}
set = listen(get_sock, 1);
用bind、listen函数开启数据流传输。
进行客户端操作关键函数
void open_ftpsrv()//客户端操作函数
{
char usr_cmd[1024];
int cmd_flag;
while(1)
{
printf("ftp_cli>");
fgets(usr_cmd,510,stdin);
fflush(stdin);
if(usr_cmd[0] == '/n')
continue;
usr_cmd[strlen(usr_cmd)-1] = '/0';
cmd_flag = ftp_usr_cmd(usr_cmd);
if(cmd_flag == 15)
{
char *cmd = strchr(usr_cmd,' ');
char dress_ftp[1024];
if(cmd == NULL)
{
printf("command error!/n");
show_help();
return;
}
else
{
while(*cmd == ' ')
cmd++;
}
if(cmd == NULL||cmd == '/0')
{
printf("command error!/n");
return;
}
else
{
char * dr = "127.0.0.1";
strncpy(dress_ftp,cmd,strlen(cmd));
dress_ftp[strlen(cmd)] = '/0';
printf("%s",dress_ftp);
if(dress_ftp == "127.0.0.1")
{
printf("Connect Seccessed!/n");
start_ftp_cmd(dr,DEFAULT_FTP_PORT);
//open成功则调用该函数,该函数与open_ftpsrv()很相似。
}
else
{
printf("Inviable Server Dress!/n");
}
}
}
else
//如果open不成功,则只能进行客户端上的操作,不能进行有关服务器操作
{
switch(cmd_flag)
{
case 11:
local_list();
memset(usr_cmd,'/0',sizeof(usr_cmd));
break;
case 12:
local_pwd();
memset(usr_cmd,'/0',sizeof(usr_cmd));
break;
case 13:
local_cd(usr_cmd);
memset(usr_cmd,'/0',sizeof(usr_cmd));
break;
case 6://quit
printf("BYE TO WEILIQI FTP!/n");
exit(0);
break;
default:
printf("command error!/n");
show_help();
memset(usr_cmd,'/0',sizeof(usr_cmd));
break;
}
}
}
}
2.目录文件操作
void ftp_list()
显示服务器当前目录下文件的函数。
int list_sock_data = xconnect_ftpdata();
创建一个新套接字,表示是否连接上服务器的数据。
ftp_send_cmd("LIST", NULL, sock_control);
ftp_get_reply(sock_control);
向服务器发送命令,得到服务器的响应答复。
new_sock = accept(list_sock_data, (struct sockaddr *)&local_host, /
(socklen_t *)&set);
从服务器接收数据(文件信息)。
void ftp_cmd_filename(char * usr_cmd, char * src_file, char * dst_file)
此函数读取客户端命令后的文件名字。在很多命令中,如get,put后要加入上传或下载的文件名字,这个函数解析文件名字信息。
void local_pwd()
显示客户端目录函数。
//print local current directory
void local_pwd()
{
char curr_dir[512];
int size = sizeof(curr_dir);
if(getcwd(curr_dir, size) == NULL)
printf("getcwd failed/n");
else
printf("Current local directory: %s/n", curr_dir);
}
getcwd(curr_dir, size)得到客户端目录。
void ftp_cd(char * usr_cmd)
转换服务器目录函数。
void ftp_cd(char * usr_cmd)
{
char *cmd = strchr(usr_cmd, ' ');
char path[1024];
if(cmd == NULL)
{
printf("command error!/n");
return;
}
else
{
while(*cmd == ' ')
cmd ++;
}
if(cmd == NULL || cmd == '/0')
{
printf("command error!/n");
return;
}
else
{
strncpy(path, cmd, strlen(cmd));
path[strlen(cmd)]='/0';
ftp_send_cmd("CWD ", path, sock_control);
ftp_get_reply(sock_control);
}
}
ftp_send_cmd("CWD ", path, sock_control);
ftp_get_reply(sock_control);
发送转换目录命令给服务器,服务器完成该功能。
创建目录函数void mkdir_srv(char * usr_cmd) 和删除目录函数void rmdir_srv(char * usr_cmd)与void ftp_cd(char * usr_cmd)函数相似,在此不做解释。
函数void local_list()列出客户端文件列表。
void local_list()
{
DIR * dp;
struct dirent *dirp;
if((dp = opendir("./")) == NULL)
{
printf("opendir() error!/n");
return;
}
printf("Local file list:/n");
while((dirp = readdir(dp)) != NULL)
{
if(strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
continue;
printf("%s/n", dirp->d_name);
}
}
转换客户端目录函数
void local_cd(char * usr_cmd)
{
char *cmd = strchr(usr_cmd, ' ');
char path[1024];
if(cmd == NULL)
{
printf("command error!/n");
return;
}
else
{
while(*cmd == ' ')
cmd ++;
}
if(cmd == NULL || cmd == '/0')
{
printf("command error!/n");
return;
}
else
{
strncpy(path, cmd, strlen(cmd));
path[strlen(cmd)]='/0';
if(chdir(path) < 0) //转换目录
printf("Local: chdir to %s error!/n", path);
else
printf("Local: chdir to %s/n", path);
}
}