在学习网络结构的知识点之后,我们可以做的小项目有很多,这里我分享一个自己实现的一个网络小项目----实现ftp协议(文件传输协议)。简单的实现从服务器中读取存在的文件名,从服务器中下载文件到当前目录,从自己的客户端实现文件上传到服务器。
1、服务器serv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include "commen.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
struct pthread_serv_msg{
pthread_t tid;
int cli_socket;
};
void *serv_for_client(void *args)
{
int ret;
struct pthread_serv_msg *pmsg=args;
int socket = pmsg->cli_socket;
struct ftp_msg serv_msg;
while(1){
memset(&serv_msg,0,sizeof(serv_msg));
ret = recv(socket,&serv_msg,sizeof(serv_msg),0);
if(ret < 0)
{
perror("recv err");
close(socket); free(pmsg);return NULL;
}else if(ret == 0)
{
close(socket); free(pmsg);return NULL;
}
//printf("serv got serv_msg:%s\n",rxbuf);
switch(serv_msg.cmd){
case 1: //list 获取当前目录下的内容 readdir()
{
DIR *dir = opendir("./");
if(dir==NULL)
{
perror("file opendir err");
return NULL;
}
int i=0,j=0;
while(1)
{
struct dirent *rent = readdir(dir);
if(rent==NULL)
{
//for(int n=0;n<i;n++)
//{
//printf("%s\n",serv_msg.response[n]);
//}
serv_msg.len=i;
break;
}
if(strcmp(rent->d_name,".")==0 || strcmp(rent->d_name,"..")==0)
{
continue;
}
//printf("%6s :%d\n",rent->d_name,rent->d_type);
strcpy(serv_msg.response[i++],rent->d_name);
//snprintf(serv_msg.response,sizeof(serv_msg.response),\
" %s %s ",serv_msg.response,rent->d_name);
}
closedir(dir);
ret = send(socket,&serv_msg,sizeof(serv_msg),0);
if(ret < 0)
{
perror("serv send err");
return NULL;
}
break;
}
case 2:
{
int fd = open(serv_msg.filename, O_CREAT|O_RDWR,0666);
if(fd < 0){
perror("open err");
return NULL;
}
while(1){
memset(serv_msg.filebuf,0,sizeof(serv_msg.filebuf));
ret = read(fd,serv_msg.filebuf,sizeof(serv_msg.filebuf)-1);
if(ret < 0 ){
perror("read err");
return NULL;
}else if(ret ==0){
//send(socket,&serv_msg,sizeof(serv_msg),0);
//printf("end of file\n");
return NULL;
}
ret = send(socket,&serv_msg,sizeof(serv_msg),0);
if(ret < 0)
{
perror("serv send err");
return NULL;
}
printf("%s",serv_msg.filebuf);
}
ret = close(fd);
if(ret <0){
perror("close err");
return NULL;
}
break;
}
case 3:
{
//创建一个空文件
int fd = open(serv_msg.filename, O_CREAT|O_RDWR,0666);
if(fd < 0)
{
perror("open err");
return NULL;
}
while(1)
{
memset(&serv_msg,0,sizeof(serv_msg));
ret = recv(socket,&serv_msg,sizeof(serv_msg),0);
if(ret < 0)
{
perror("recv err");
return NULL;
}
ret = write(fd,serv_msg.filebuf,strlen(serv_msg.filebuf));
if(ret < 0 ){
perror("write err");
return NULL;
}
//如果发现对方最后一次,结束while
if(ret<sizeof(serv_msg.filebuf))
{
printf("upload success\n");
break;
}
}
ret = close(fd);
if(ret <0){
perror("close err");
return NULL;
}
break;
}
}
}
close(socket);
free(pmsg);
return NULL;
}
int main()
{
int socket_main = socket(AF_INET,SOCK_STREAM,0);
if(socket_main < 0)
{
perror("socket creat err");
return -1;
}
printf("socket success\n");
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(10090);
serv_addr.sin_addr.s_addr = 0;//inet_addr("192.168.3.76");
int ret = bind(socket_main, (struct sockaddr *)&serv_addr , sizeof(serv_addr));
if(ret < 0)
{
perror("bind err");
return -3;
}
printf("bind success\n");
struct sockaddr_in cli_addr;
socklen_t addrlen=sizeof(cli_addr);
struct pthread_serv_msg *pserv_msg=NULL;
int socket_new;
ret = listen(socket_main,5);
if(ret < 0)
{
perror("listen err");
return -3;
}
printf("listen......\n");
loop:
socket_new = accept(socket_main,(struct sockaddr *)&cli_addr,&addrlen);
if(socket_new < 0)
{
perror("accept err");
return -3;
}
printf("accpet success\n");
printf("serv accpet client ip=%s port=%d\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));
pserv_msg=malloc(sizeof(*pserv_msg));
pserv_msg->cli_socket=socket_new;
pthread_create(&pserv_msg->tid,NULL,serv_for_client,pserv_msg);
pthread_detach(pserv_msg->tid);
printf("success link\n");
goto loop;
close(socket_main);
return 0;
}
2、客户端cli.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include "commen.h"
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/*
1.获取服务文件 ./cli.out list
2.下载文件 ./cli.out download filename
3.上传文件 ./cli.out upload filename
*/
struct ftp_msg cli_msg;
int main(int argc,char **argv)
{
//创建套接字
int socket_clie = socket(AF_INET,SOCK_STREAM,0);
if(socket_clie < 0)
{
perror("socket creat err");
return -1;
}
printf("socket success\n");
//与服务器建立联系
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(10090);
serv_addr.sin_addr.s_addr = 0;//inet_addr("192.168.3.76");
int ret = connect(socket_clie,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
if(ret < 0)
{
perror("connect err");
return -3;
}
printf("connect success\n");
memset(&cli_msg,0,sizeof(cli_msg));
if( strcmp(argv[1],"list")==0 ){
memset(&cli_msg,0,sizeof(cli_msg));
cli_msg.cmd=1;
//发送指令
ret = send(socket_clie,&cli_msg,sizeof(cli_msg),0);
if(ret < 0)
{
perror("send err");
return -3;
}
//等待服务器回应
memset(&cli_msg,0,sizeof(cli_msg));
ret = recv(socket_clie,&cli_msg,sizeof(cli_msg),0);
if(ret < 0)
{
perror("recv err");
return -3;
}
for(int n=0;n<cli_msg.len;n++)
{
printf("%s\n",cli_msg.response[n]);
}
//printf("111\n");
}else if( strcmp(argv[1],"download")==0 ){
memset(&cli_msg,0,sizeof(cli_msg));
cli_msg.cmd=2;
strcpy(cli_msg.filename,argv[2]);
//发送指令
ret = send(socket_clie,&cli_msg,sizeof(cli_msg),0);
if(ret < 0)
{
perror("send err");
return -3;
}
//创建一个空文件
int fd = open(cli_msg.filename, O_CREAT|O_RDWR,0666);
if(fd < 0)
{
perror("open err");
return -3;
}
while(1)
{
memset(&cli_msg,0,sizeof(cli_msg));
ret = recv(socket_clie,&cli_msg,sizeof(cli_msg),0);
if(ret < 0)
{
perror("recv err");
return -3;
}
ret = write(fd,cli_msg.filebuf,strlen(cli_msg.filebuf));
if(ret < 0 ){
perror("write err");
return -9;
}
//如果发现对方最后一次,结束while
if(ret<sizeof(cli_msg.filebuf)-1)
{
printf("download success\n");
break;
}
}
ret = close(fd);
if(ret <0){
perror("close err");
return -3;
}
}else if( strcmp(argv[1],"upload")==0 ){
memset(&cli_msg,0,sizeof(cli_msg));
cli_msg.cmd=3;
strcpy(cli_msg.filename,argv[2]);
//发送指令
ret = send(socket_clie,&cli_msg,sizeof(cli_msg),0);
if(ret < 0)
{
perror("send err");
return -3;
}
//打开上传的文件夹
int fd = open(cli_msg.filename, O_CREAT|O_RDWR,0666);
if(fd < 0){
perror("open err");
return -1;
}
while(1){
memset(cli_msg.filebuf,0,sizeof(cli_msg.filebuf));
ret = read(fd,cli_msg.filebuf,sizeof(cli_msg.filebuf)-1);
if(ret < 0 ){
perror("read err");
return -1;
}else if(ret ==0){
send(socket_clie,&cli_msg,sizeof(cli_msg),0);
//printf("end of file\n");
return -1;
}
ret = send(socket_clie,&cli_msg,sizeof(cli_msg),0);
if(ret < 0)
{
perror("serv send err");
return -1;
}
//printf("%s",cli_msg.filebuf);
}
ret = close(fd);
if(ret <0){
perror("close err");
return -1;
}
}else {
//bug;
printf("输入的格式有误!!!\n");
return 0;
}
return 0;
}
3、头文件commen.c
#ifndef __COMMON_XXX_
#define __COMMON_XXX_
struct ftp_msg {
char cmd;
char filename[32];
char filebuf[128];
int ret;
int len;
char response[32][32];
int endfileflag;
};
#endif
在编译服务器的时候需要加入外部库,-lpthread。
客户端执行时需要在后面添加参数:./执行文件 指令名称 文件名。