基于Linux的客户端与服务端的传输和信息交互
程序员文章站
2022-03-04 08:53:32
...
基于Linux的客户端与服务端的传输和信息交互,利用socket网络来构建服务端和客户端的网络请求,在服务端接收到客户端的连接请求的时候创建进程用于,服务端与多个客户端之间的连接,在头文件中穿件用于网络传输数据的结构体,其中包括具体的命令(cmd),传输文件时文件中的内容(context),以及用于判断在传输后是否需要创建文件的标志位;利用字符串处理函数来分解指令,获得具体指令,再根据指令的类型来选择需要执行的代码,实现服务端与客户端之间的文件传输和信息交互;
头文件中包含宏定义各个指定,和数据传输过程中的结构体:
parament.h
#define LS 1
#define CD 2
#define PWD 3
#define LLS 4
#define LCD 5
#define DOWNLOAD 6
#define UPLOAD 7
#define QUIT 8
struct command{
char cmd[128]; //具体命令
char context[1024]; //用于存放服务端传送给客户端的具体内容
int flag; //客户端需要穿件文件的标记位
};
服务端:
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include<arpa/inet.h>
#include <netinet/in.h>
#include<string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include "parament.h"
int getCmdType(char *cmd) //获取命令类型,并返回宏定义的整形数,用于取命令中switch
{
if(strcmp("ls",cmd) == 0){
return LS;
}
if(strcmp("quit",cmd) == 0){
return QUIT;
}
if(strcmp("pwd",cmd) == 0){
return PWD;
}
if(strstr(cmd,"cd") != NULL){
return CD;
}
if(strstr(cmd,"download") != NULL){
return DOWNLOAD;
}
if(strstr(cmd,"upload") != NULL){
return UPLOAD;
}
return 0;
}
char * cmdDetail(char *cmd) //利用字符串处理函数将命令细节提取出来
{
char *temp;
temp = strtok(cmd," ");
temp = strtok(NULL," ");
return temp;
}
void comHandler(struct command Cmd,int fd) //命令处理函数,参数为从客户端传过来的数据和用于连接的文件描述符
{
int ret = getCmdType(Cmd.cmd); //首先获取命令类型
char filename[128];
int filefd;
switch(ret){ //判断指令
case LS: //显示服务端文件
case PWD: //显示服务端当前路径
Cmd.flag = 0; //这个标记为是用于客户端判断是否需要穿件文件(download),服务端会发送过来数据
FILE *f = popen(Cmd.cmd,"r"); //因为需要将内容在客户端打印出来,所以使用popen将执行结果放入FILE流中
fread(Cmd.context,1,sizeof(Cmd.context),f); //将结果放置到数据传输结构体的具体内容中
write(fd,&Cmd,sizeof(Cmd)); //将传输结构体发送到客户端
printf("you get my path or file\n"); //在服务端打印提示信息
break;
case CD: //服务端进入文件夹
Cmd.flag =0; //该操作不需要客户端创建文件
strcpy(filename,cmdDetail(Cmd.cmd)); //获取客户端发送过来的命令中具体路径
chdir(filename); //利用chdir进入文件夹
printf("into %s\n",filename); //打印提示命令
break;
case DOWNLOAD: //客户端需要从服务端下载文件
strcpy(filename,cmdDetail(Cmd.cmd)); //获取具体文件名
if(access(filename,F_OK) == -1){ //判断是否有该文件
Cmd.flag = -1; //没有该文件设置标志位为-1
strcpy(Cmd.cmd,"server No such file"); //将传输数据的结构体输入信息发送给客户端
write(fd,&Cmd,sizeof(Cmd));
}else{
Cmd.flag = 1; //需要服务端发送文件信息到客户端,所以需要把结构体标志位置1
filefd = open(filename,O_RDWR); //以可读可写方式打开文件
read(filefd,Cmd.context,sizeof(Cmd.context)); //读取文件信息到传输的结构体中的具体内容中
close(filefd); //关闭文件
write(fd,&Cmd,sizeof(Cmd)); //发送信息到客户端
}
printf("put file to %d client success\n",fd);
break;
case UPLOAD: //从客户端传输过来的文件
Cmd.flag = 0;
strcpy(filename,cmdDetail(Cmd.cmd));
filefd = open(filename,O_CREAT|O_RDWR,0666);
write(filefd,Cmd.context,sizeof(Cmd.context));
close(filefd);
printf("get file from %d client success\n",fd);
break;
case QUIT: //客户端退出
Cmd.flag = 0;
printf("client fd quit\n");
exit(-1);
break;
case 0: //发送错误命令
printf("command is wrong\n");
break;
}
}
int main()
{
int s_fd;
int bind_ret;
int listen_ret;
int nread;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8899);
inet_aton("192.168.191.3",&s_addr.sin_addr);
//socket
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd ==-1){
perror("socket");
exit(-1);
}
//bind
bind_ret = bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
if(bind_ret == -1){
perror("bind");
exit(-1);
}
//listen
listen_ret = listen_ret = listen(s_fd,10);
if(listen_ret == -1){
perror("listen");
exit(-1);
}
//accept
int c_addr_len = sizeof(struct sockaddr_in); //以上为socket经典操作
int c_fd;
struct command readBuf; //定义一个命令结构体
while(1){ //创建一个死循环让服务端一直接受客户端连接
c_fd= accept(s_fd,(struct sockaddr *)&c_addr,&c_addr_len); //获取到一个客户端连接
if(c_fd == -1){
perror("accept error:");
}
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0){ //当有一个客户端连接上时创建一个子进程
while(1){ //子进程中不断读取客户端发送过来的信息并做相应处理
memset(readBuf.cmd,0,sizeof(readBuf.cmd));
nread = read(c_fd,&readBuf,sizeof(readBuf));
if(nread == 0){
printf("client out\n");
break;
}else if(nread >0){
printf("from client:%s message:%s",inet_ntoa(c_addr.sin_addr),readBuf.cmd);
comHandler(readBuf,c_fd);
}
}
}
}
close(c_fd);
close(s_fd);
return 0;
}
客户端:
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include<linux/in.h>
#include<arpa/inet.h>
#include <netinet/in.h>
#include<string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "parament.h"
int FLAG; //该全局变量用于客户端在发送完数据之后是否需要接受服务端的数据
int getCmdType(char *cmd)
{
if(strcmp("ls",cmd) == 0){
return LS;
}
if(strcmp("quit",cmd) == 0){
return QUIT;
}
if(strcmp("lls",cmd) == 0){
return LLS;
}
if(strstr("lcd",cmd) != NULL){
return LCD;
}
if(strcmp("pwd",cmd) == 0){
return PWD;
}
if(strstr(cmd,"cd") != NULL && strstr(cmd,"lcd") == NULL){
return CD;
}
if(strstr(cmd,"download") != NULL){
return DOWNLOAD;
}
if(strstr(cmd,"upload") != NULL){
return UPLOAD;
}
return 0;
}
char * cmdDetail(char *cmd)
{
char *temp;
temp = strtok(cmd," ");
temp = strtok(NULL," ");
return temp;
}
void cmdHandler(struct command Cmd,int fd)
{
int CMD = getCmdType(Cmd.cmd);
char ret[128] = {'\0'};
int filefd;
switch(CMD){
case LS:
case PWD:
case CD:
FLAG = 1; //需要打印路径,文件名,所以需要在发送完数据之后再接受服务端发送过来的数据
write(fd,&Cmd,sizeof(Cmd));
break;
case DOWNLOAD:
FLAG=1; //下载服务端中的某个文件
write(fd,&Cmd,sizeof(Cmd));
break;
case UPLOAD: //将某个文件传输给服务端
FLAG=0;
strcpy(ret,cmdDetail(Cmd.cmd));
if(access(ret,F_OK) == -1){ //判断客户端是否有该文件
printf("client No such file\n");
}
filefd = open(ret,O_RDWR);
memset(Cmd.context,0,sizeof(Cmd.context));
read(filefd,Cmd.context,sizeof(Cmd.context));
write(fd,&Cmd,sizeof(Cmd));
break;
case LLS: //客户端展示文件夹中的文件
FLAG=0;
system("ls");
break;
case LCD: //客户端进入某个文件夹
FLAG=0;
strcpy(ret,cmdDetail(Cmd.cmd));
chdir(ret);
break;
case QUIT: //客户端退出
FLAG=0;
strcpy(Cmd.cmd,"quit");
write(fd,&Cmd,sizeof(Cmd));
close(fd);
exit(-1);
break;
}
}
void cmdFromServerhandler(struct command recv,int fd) //客户端在发送完消息后有的命令需要接受服务端的内容所以需要该函数
{
int nread = read(fd,&recv,sizeof(recv));
int filefd;
if(recv.flag == 1){ //结构体中的标记位为1,所以需要创建文件来写入客户端传入的数据
char *filename = cmdDetail(recv.cmd);
filefd = open(filename,O_CREAT|O_RDWR,0666);
write(filefd,recv.context,sizeof(recv.context));
}else{ //否则打印传输过来的结构体中的具体数据
printf("=============================\n");
printf("%s\n",recv.context);
printf("=============================\n");
}
}
int main(int argc,char **argv)
{
int c_fd;
int connect_ret;
struct command sendBuf;
struct command receBuf;
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
//socket
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd ==-1){
perror("socket");
exit(-1);
}
//connect
connect_ret = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));
if(connect_ret == -1){
perror("connect");
exit(-1);
}else{
printf("connect success");
}
while(1){
printf("please input command:");
memset(sendBuf.cmd,0,sizeof(sendBuf.cmd));
scanf("%s",sendBuf.cmd); //输入命令到结构体cmd中
cmdHandler(sendBuf,c_fd); //调用处理函数
if(FLAG == 0){ //根据全局变量判断是否需要处理服务端发送过来的数据
continue;
}
cmdFromServerhandler(receBuf,c_fd); //服务端发送过来的数据处理函数
}
return 0;
}