主要代码
config.h
#define LS 0
#define GET 1
#define PWD 2
#define IFGO 3
#define LCD 4
#define LLS 5
#define CD 6
#define PUT 7
#define QUIT 8
#define DOFILE 9
struct Msg
{
int type;
char data[1024];
char secondBuf[128];
};
服务器:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// #include<linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include <sys/stat.h>
#include <fcntl.h>
int get_cmd_type(char *cmd)
{
if (!strcmp("ls", cmd))
return LS; // 这里类命令没有参数直接strcmp 比较即可
if (!strcmp("quit", cmd))
return QUIT;
if (!strcmp("pwd", cmd))
return PWD;
if (strstr(cmd, "cd") != NULL)
return CD; 对于有参数的命令,则去 寻找他的子串是否包含这个命令
if (strstr(cmd, "get") != NULL)
return GET;
if (strstr(cmd, "put") != NULL)
return PUT;
}
char *getDesDir(char *cmsg) // 获取后段字符串 --- 参数
{
char *p = strtok(cmsg, " ");
p = strtok(NULL, " ");
return p;
}
void msg_handler(struct Msg msg, int fd)
{
char dataBuf[1024] = {0};
char *file = NULL;
int fdfile;
printf("cmd: %s\n", msg.data);
int ret = get_cmd_type(msg.data);
switch (ret)
{ // 根据返回的指令选择,处理的方式
case LS:
case PWD:
// popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程。
FILE *r = popen(msg.data, "r");
fread(msg.data, sizeof(msg.data), 1, r);
write(fd, &msg, sizeof(msg)); // 写入socket通道的 fd
break;
case CD:
char *dir = getDesDir(msg.data);
printf("dir: %s\n", dir);
chdir(dir);
break;
case GET:
msg.type=DOFILE;
file = getDesDir(msg.data); 获取后段字符串 --- 参数 -文件名
if (access(file, F_OK) == -1)
{ // access 判断文件是否存在
strcpy(msg.data, "No this file!"); 将 "no this file"的信息写入data,并且write返回给客户端
write(fd, &msg, sizeof(msg));
}
else
{
// 存在,打开服务器目录下的文件,读取里面的内容,写入到客户端文件中
fdfile = open(file, O_RDWR); // 打开服务器目录下的文件
read(fdfile, dataBuf, sizeof(dataBuf)); // 读取里面的内容
close(fdfile); // 用完记得关闭文件
strcpy(msg.data, dataBuf); // 写入数据
write(fd, &msg, sizeof(msg)); // 写入到客户端文件中
}
break;
case PUT: // 上传文件到服务器
fdfile = open(getDesDir(msg.data), O_RDWR | O_CREAT, 0666); // 以可读可写的形式创建 需要put的文件,给0666的权限 -- 可读可写
write(fdfile, msg.secondBuf, strlen(msg.secondBuf)); 向新创建的文件中写入数据,secondBuf
close(fdfile);
break;
case QUIT:
puts("Client Quit!");
exit(-1);
}
}
int main(int argc, char **argv)
{
int s_fd;
int c_fd;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
int nread;
// char msg[128]={0};
struct Msg msg;
char readBuf[128] = {0};
if (argc != 3)
{ // 判断传参是否正确
puts("Improper parameters");
exit(-1);
}
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
int clen = sizeof(struct sockaddr_in);
// 1. socket -- IPv4 流形式i 0 --默认 TCP
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1)
{
perror("socket");
exit(-1);
}
// 初始化bind需要的struct
s_addr.sin_family = AF_INET;
// htons -- h - host to ns-net short
s_addr.sin_port = htons(atoi(argv[2])); // 通过传参指定端口
// a -- ASCLL 传参方式得到IP
inet_aton(argv[1], &s_addr.sin_addr);
// 3.bind
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
// listen
listen(s_fd, 10);
// 4.accept
while (1)
{
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen); // 获得新的客户端 描述符
if (c_fd == -1)
{
perror("accept");
}
printf("get connect. %s\n", inet_ntoa(c_addr.sin_addr));
if (fork() == 0)
{ // 接入新的客户端 创建子进程来处理读
while (1)
{
// read()
memset(msg.data, 0, sizeof(msg.data));
nread = read(c_fd, &msg, sizeof(msg));
if (nread == 0)
{
printf("client out\n"); // 读取时已经到达文件的末尾,则返回0。表示客户端退出
break;
}
else if (nread > 0) // 读取成功
{
msg_handler(msg, c_fd);
}
}
}
}
close(c_fd);
close(s_fd);
return 0;
}
客户端:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
// #include<linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include <sys/stat.h>
#include <fcntl.h>
int get_cmd_type(char *cmd)
{
if (strstr(cmd, "lcd"))
return LCD;
if (strstr(cmd, "cd"))
return CD;
if (strstr(cmd, "get"))
return GET;
if (strstr(cmd, "put"))
return PUT;
if (!strcmp("quit", cmd))
return QUIT;
if (!strcmp("ls", cmd))
return LS;
if (!strcmp("lls", cmd))
return LLS;
if (!strcmp("pwd", cmd))
return PWD;
return -1; // 找不到这个指令就放回-1
}
char *getdir(char *cmsg) // 获取后段字符串 --- 参数
{
char *p = strtok(cmsg, " ");
p = strtok(NULL, " ");
return p;
}
int cmdHanler(struct Msg msg, int fd)
{
char buf[32];
char *dir = NULL;
int ret;
int fdfile;
ret = get_cmd_type(msg.data);
switch (ret)
{
case LS:
case CD:
case PWD:
write(fd, &msg, sizeof(msg));
break;
case GET:
write(fd, &msg, sizeof(msg));
break;
case PUT:
strcpy(buf, msg.data);
dir = getdir(buf);
if (access(dir, F_OK) == -1)
{
printf("%s not exit\n", dir);
}
else
{
fdfile = open(dir, O_RDWR);
read(fdfile, msg.secondBuf, sizeof(msg.secondBuf)); 客户端把数据读到secondBuf里面
close(fdfile);
write(fd, &msg, sizeof(msg)); // 将内容写给服务器fd
}
break;
case LLS:
system("ls");
break;
case LCD:
dir = getdir(msg.data);
chdir(dir);
break;
case QUIT:
strcpy(msg.data, "quit");
write(fd, &msg, sizeof(msg));
close(fd);
exit(-1);
}
return ret;
}
void handlerServerMessage(int c_fd,struct Msg msg){
int nread;
struct Msg msgget;
int newfdfile;
nread=read(c_fd,&msgget,sizeof(msgget));
if(nread==0){
printf("server is out,quit\n");
exit(-1);
}
else if(msgget.type == DOFILE)
{
char *p=getdir(msg.data);
newfdfile=open(p,O_RDWR|O_CREAT,0600);
write(newfdfile,msgget.data,strlen(msgget.data));
putchar('>');
fflush(stdout);
}
else{
printf("----------------------------------\n");
printf("\n%s\n",msgget.data);
printf("----------------------------------\n");
putchar('>');
fflush(stdout);
}
}
int main(int argc, char **argv)
{
int c_fd;
struct sockaddr_in c_addr;
struct Msg msg;
int nread;
if (argc != 3)
{ // 判断传参是否正确
puts("Improper parameters");
exit(-1);
}
// char msg[128] = {0};
char readBuf[128];
memset(&c_addr, 0, sizeof(struct sockaddr_in));
int clen = sizeof(struct sockaddr_in);
// 1. socket -- IPv4 流形式i 0 --默认 TCP
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1)
{
perror("socket");
exit(-1);
}
// 初始化bind需要的struct
c_addr.sin_family = AF_INET;
// htons -- h - host to ns-net short
c_addr.sin_port = htons(atoi(argv[2]));
// a -- ASCLL
inet_aton(argv[1], &c_addr.sin_addr);
// 2.connect -- 阻塞等待连接
int p_c = connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in));
if (p_c == -1)
{
perror("connect");
exit(-1);
}
printf("get connect. %s\n", inet_ntoa(c_addr.sin_addr));
while (1)
{ // 我们希望能一直读写 -- 实现聊天功能
memset(msg.data, 0, sizeof(msg.data));
gets(msg.data); // 一直 获取 命令输入
int ret = cmdHanler(msg, c_fd);
if (ret > IFGO)
{
putchar('>');
fflush(stdout);
continue;
}
if (ret == -1)
{
puts("command not");
putchar('>');
fflush(stdout);
continue;
}
handlerServerMessage(c_fd, msg);
}
return 0;
}
代码解析:
基本命令
如下图:
我们在客户端中将LS PWD CD命令写入msg,到服务器这边就会读取他们的值
msg.data 的第一段子串是命令,第二串是参数 -- 比如 cd ..
ls,pwd只有命令,调用popen来调用
popen()函数补充
popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程。这个进程必须由 pclose() 函数关闭,而不是 fclose() 函数。pclose() 函数关闭标准 I/O 流,等待命令执行结束,然后返回 shell 的终止状态。如果 shell 不能被执行,则 pclose() 返回的终止状态与 shell 已执行 exit 一样。
也就是,popen创建管道,执行shell命令将文件流中的某些数据读出
get
服务器先将文件写入msg中
客户端再通过msg获取
//这个函数用来读取我们的文件指令
if 类型是文件操作 FOFILE,就创建创建文件向其中写入数据,这样就得到了服务器的文件 --GET
else 直接将操作打印出来
put:
如下图,先从客户端开始说,客户端先确保,文件存在,然后将文件打开,将文件内容读取到secondBuf里面,关文件,写入fd(服务器)的msg中。
服务器,直接创建一个文件来接收我们刚刚传过来的数据,写入文件,关文件。
_access()函数补充
头文件:#include<io.h>
函数原型:int _access(const char *pathname, int mode);
参数:pathname 为文件路径或目录路径,mode 为访问权限
返回值:如果文件具有指定的访问权限,则函数返回0;如果文件不存在或者不能访问指定的权限,则返回-1
lcd:
读取到dir 为第二个子串 -- 参数
调用chdir 跳转即可
chdir()函数 补充
系统调用 chdir()和 fchdir()可以用于更改进程的当前工作目录,函数原型如下所示:
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);
首先,使用这两个函数之一需要包含头文件<unistd.h>
函数参数和返回值含义如下:
path:将进程的当前工作目录更改为 path 参数指定的目录,可以是绝对路径、也可以是相对路径,指定的目录必须要存在,否则会报错。
fd:将进程的当前工作目录更改为 fd 文件描述符所指定的目录(譬如使用 open 函数打开一个目录)。
返回值:成功均返回 0;失败均返回-1,并设置 errno。
// 其余细节,还请读者看代码注释