程序介绍
该程序是建立在局域网下,模拟服务器与客户端之间通信的方式,可供选择的功能包括接收文件、显示当前路径、更换路径等。如果使用虚拟机的话,请使用桥接模式进行网络配置,相关配置方法如链接所示:VMware虚拟机网络配置—桥接模式
演示步骤
1.查询网络ip
本文以虚拟机进行代替,严谨地说,应该采用局域网下的两个不同的电脑端进行收发信息,但只是为了解释相关效果以及程序,所以方便起见,以虚拟机不同路径下进行测试。
首先在服务端的Linux系统上输入: ifconfig
查询服务端的ip地址,即192.168.175.128。
lamda@lamda-virtual-machine:~/Desktop$ ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.175.128 netmask 255.255.255.0 broadcast 192.168.175.255
inet6 fe80::20c:29ff:fe39:171c prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:39:17:1c txqueuelen 1000 (Ethernet)
RX packets 157250 bytes 218860850 (218.8 MB)
RX errors 1429 dropped 33 overruns 0 frame 0
TX packets 66801 bytes 3858581 (3.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 19 base 0x2000
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 3473 bytes 316259 (316.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3473 bytes 316259 (316.2 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
2.启动服务端和客户端,展示效果
服务端:
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make$ ls
command.h demo_server.c kehu server
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make$ gcc demo_server.c -o server
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make$ ./server 192.168.175.128 8983 //创建网络地址等待连接,一般选择服务端ip地址
connect: NO.1 client ip_addr = 192.168.175.128 //获取客户端ip地址
客户端:
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make/kehu$ ls
client demo_client.c demo_mutex.c
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make/kehu$ gcc demo_client.c -o client
lamda@lamda-virtual-machine:~/Desktop/code/Linux/Socket/ftp_make/kehu$ ./client 192.168.175.128 8983
=========================================
> help
The included commands are as follows:
ls-----list directory contents of current path from server.
lls----list directory contents of current path from client.
pwd----print name of current directory from server.
get----get file from server.
cd-----change the working directory.
quit---quit this program.
=========================================
> ls
command.h
demo_server.c
kehu
server
=========================================
> lls
client demo_client.c demo_mutex.c
=========================================
> pwd
/home/lamda/Desktop/code/Linux/Socket/ftp_make
=========================================
> cd ..
=========================================
> ls
client
demo_client.c
demo_server.c
ftp_make
server
=========================================
> cd ../Thread
=========================================
> ls
a.out
demo_cond.c
demo_mutex.c
demo_thread.c
=========================================
> get demo_cond.c
=========================================
> quit
Program is over.
3.相关代码
服务端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "command.h"
#include <sys/stat.h>
#include <fcntl.h>
char *take(char *readbuf) {
char *str;
str = strtok(readbuf," ");
str = strtok(NULL," ");
return str;
}
void handle(int num,char *readbuf,char *sendbuf) {
FILE *fps;
char *str;
int fd;
switch (num) {
case LS:
fps = popen(readbuf,"r");
fread(sendbuf,sizeof(char),1024,fps);
pclose(fps);
break;
case PATH:
fps = popen(readbuf,"r");
fread(sendbuf,sizeof(char),1024,fps);
pclose(fps);
break;
case CD:
str = take(readbuf);
chdir(str);
strcpy(sendbuf," ");
break;
case GET:
str = take(readbuf);
fd = open(str,O_RDWR);
if(fd==-1){
printf("This file maybe not exist.\n");
}
else{
int num_seek = lseek(fd,0,SEEK_END);
printf("num_seek = %d\n",num_seek);
lseek(fd,0,SEEK_SET);
read(fd,sendbuf,num_seek);
}
close(fd);
break;
case PUT:
strcpy(sendbuf," ");
break;
case HELP:
strcpy(sendbuf," ");
break;
case QUIT:
printf("Program is over.\n");
strcpy(sendbuf," ");
break;
default:
strcpy(sendbuf,"command has not been defined");
break;
}
}
int analysis(char *readbuf) {
int com_num;
if(strcmp(readbuf,"ls")==0) com_num = LS;
else if(strcmp(readbuf,"lls")==0) com_num = LLS;
else if(strcmp(readbuf,"pwd")==0) com_num = PATH;
else if(strcmp(readbuf,"quit")==0) com_num = QUIT;
else if(strcmp(readbuf,"help")==0) com_num = HELP;
else if(strstr(readbuf,"get")!=NULL) com_num = GET;
else if(strstr(readbuf,"put")!=NULL) com_num = PUT;
else if(strstr(readbuf,"cd")!=NULL) com_num = CD;
else {
com_num = -1;
}
return com_num;
}
int main(int argc,char **argv) {
if(argc != 3) {
printf("prarmeter num is wrong.\n");
exit(-1);
}
// socket int socket(int domain, int type, int protocol);
int socket_fd = socket(AF_INET,SOCK_STREAM,0);
if(socket_fd == -1) {
perror("Socket");
exit(-1);
}
// bind int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
struct sockaddr_in my_addr,client_addr;
memset(&my_addr,0,sizeof(struct sockaddr_in));
memset(&client_addr,0,sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&my_addr.sin_addr);
int bind_check = bind(socket_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr_in));
if(bind_check ==-1){
perror("Bind");
exit(-1);
}
// listen int listen(int sockfd, int backlog);
listen(socket_fd,10);
int client_num = 0;
// accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//int connect_fd = accept(socket_fd,NULL,NULL);
unsigned int addrlen = sizeof(struct sockaddr_in);
int client_fd = accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);
if(client_fd ==-1)
{
perror("accept");
exit(-1);
}
client_num++;
printf("connect: NO.%d client ip_addr = %s\n",client_num,inet_ntoa(client_addr.sin_addr));
char readbuf[128]={0};
char sendbuf[102400]={0};
int flags = 1;
if(vfork()==0){
// read
while(flags) {
memset(readbuf,0,sizeof(readbuf));
int n_read = read(client_fd,readbuf,sizeof(readbuf));
if(n_read == -1){
perror("Read");
exit(-1);
}
printf("%s\n",readbuf);
memset(sendbuf,0,sizeof(sendbuf));
int num = analysis(readbuf);
if(num == QUIT)
flags = 0;
handle(num,readbuf,sendbuf);
int n_write = write(client_fd,sendbuf,strlen(sendbuf));
}
exit(3);
}
// close
close(socket_fd);
return 0;
}
command.h //用来解析命令,给命令赋值
#ifndef COMMAND_H_
#define COMMAND_H_
#define LS 0
#define LLS 1
#define PATH 2
#define QUIT 3
#define HELP 4
#define LINE 5
#define GET 6
#define PUT 7
#define CD 8
#endif
客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
void check(char * argv) {
int size = strlen(argv);
argv[size-1] = '\0';
}
char *take(char *readbuf) {
char *str;
str = strtok(readbuf," ");
str = strtok(NULL," ");
return str;
}
int main(int argc,char **argv) {
if(argc != 3) {
printf("prarmeter num is wrong.\n");
exit(-1);
}
// socket int socket(int domain, int type, int protocol);
int socket_fd = socket(AF_INET,SOCK_STREAM,0);
if(socket_fd == -1) {
perror("Socket");
exit(-1);
}
// connect int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
struct sockaddr_in my_addr;
memset(&my_addr,0,sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&my_addr.sin_addr);
int connect_fd = connect(socket_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr_in));
if(connect_fd ==-1){
perror("Connect");
exit(-1);
}
int flags = 1;
char sendbuf[128] = {0};
char readbuf[102400] = {0};
if(vfork()==0){
while(flags){
memset(sendbuf,0,sizeof(sendbuf));
printf("=========================================\n");
printf("> ");
fgets(sendbuf,sizeof(sendbuf),stdin);
check(sendbuf);
//printf("You send : %s\n",sendbuf);
if(strcmp(sendbuf,"quit")==0) {
flags = 0;
printf("Program is over.\n");
}
else if(strcmp(sendbuf,"lls")==0)
system("ls");
else if(strcmp(sendbuf,"help")==0) {
printf("The included commands are as follows:\n");
printf("ls-----list directory contents of current path from server.\n");
printf("lls----list directory contents of current path from client.\n");
printf("pwd----print name of current directory from server.\n");
printf("get----get file from server.\n");
printf("cd-----change the working directory.\n");
printf("quit---quit this program.\n");
}
else if(strstr(sendbuf,"get")!=NULL) {
int n_write = write(socket_fd,sendbuf,strlen(sendbuf));
memset(readbuf,0,sizeof(readbuf));
int n_read = read(socket_fd,readbuf,sizeof(readbuf));
if(n_read == -1){
perror("Read");
exit(-1);
}
FILE *fp = fopen(take(sendbuf),"a+");
if(fp ==NULL)
{
perror("get file");
continue;
}
fwrite(readbuf,sizeof(char),strlen(readbuf),fp);
fclose(fp);
}
else{
int n_write = write(socket_fd,sendbuf,strlen(sendbuf));
memset(readbuf,0,sizeof(readbuf));
int n_read = read(socket_fd,readbuf,sizeof(readbuf));
if(n_read == -1){
perror("Read");
exit(-1);
}
puts(readbuf);
}
}
exit(3);
}
// close
close(socket_fd);
return 0;
}
4.代码使用函数介绍
(1)服务端开启后,需要双方一直保持收发消息直至程序退出,所以当连接成功后,客户端和服务端各自开启一个子进程进行对接,客户端发送消息后,还要准备接收消息,服务端负责接收消息并给出相应操作;在这里必须保证子进程运行期间父进程不能抢占资源,而是让子进程一直在等待接收消息,所以使用vfork函数,也可以用fork函数搭配wait使用。
(2)当采用cd,get等命令时,需要截取字符串空格后的内容,所以采用strtok函数,具体使用方法及原理可参照:C 库函数 - strtok()
(3)函数命令解析用到command.h文件,所以服务端必须与该文件在同一目录下进行编译。
(4)此项目还可以添加其他命令,例如实现vi命令,可分若干步进行操作:1.客户端接收文件2.调用系统命令,在客户端本地进行vi操作3.传输文件到服务端,目前仍在尝试加入。