目录
1.项目描述
2.函数准备
2.1 gets函数
2.2 popen函数、fread函数
2.3 access 函数
2.4 exit 函数
2.5 strtok 函数
2.6 chdir函数
3.项目代码
3.1服务器代码
3.2客户端代码
4.问题总结
1.项目描述
基于Soket聊天服务器,实现服务器和客户端的文件传输。
Linux系统下建立Socket聊天服务器_趣知boy的博客-CSDN博客
ls 获取服务器文件列表
pwd 获取服务器当前路径
cd 对服务器目录的操作 +dir
quit 退出连接
put 上传文件到服务器 +file_name
get 获取服务器数据 +file_name
lcd 对客户端目录的操作 +dir
lls 列出客户端所有文件
dofile 创建文件
项目结构
2.函数准备
2.1 gets函数
gets 从标准输入流(通常是键盘)读取一行字符串,并将其存储在指定的字符数组中。
其比较重要的一个功能是阻塞
printf("请输入您的姓名:");
gets(name);
printf("您的姓名是:%s\n", name);
2.2 popen函数、fread函数
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
该函数可以读取指定数量(count = 1)的元素,每个元素的大小为size个字节,从指定文件(stream)中读取到内存指针(ptr)所指的位置。
2.3 access 函数
用于判断文件或目录是否具有某种权限。
int access(const char *pathname, int mode);
F_OK:检查文件是否存在
R_OK:检查读权限是否存在
W_OK:检查写权限是否存在
X_OK:检查执行权限是否存在
返回值:
-
如果路径名指定的文件或目录具有所需的权限,则返回0。
-
如果权限不足,则返回-1,并且errno设置为适当的错误码
2.4 exit 函数
exit函数是一个用于终止程序运行的函数。当调用exit函数时,程序将立即退出并返回到操作系统。
2.5 strtok 函数
char *strtok(char *str, const char *delim);
其中str是要进行分割的字符串,delim是作为分隔符的字符串。函数返回一个指向被分割出的子字符串的指针。
strtok函数陷阱:会改变指针指向的字符串,所以想保留原来str需要用strcpy复制出来处理。
token = strtok(str, ",");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ",");
}
return 0;
执行上述代码会输出以下结果:
Hello
World
This
is
Strtok
2.6 chdir函数
更改当前工作目录
int chdir(const char *path);
参 数:Path 目标目录,可以是绝对目录或相对目录。
返回值:成功返回0 ,失败返回-1
3.项目代码
3.1服务器代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct Msg msg;
int ss_fd;
void handler_cmd(char *cmd){
int cmd_int,fd;
char *p;
FILE *file;
char *cmd_pre=cmd;
//handle the cmd to number
if(!strcmp(cmd_pre,"ls")) cmd_int=ls;
if(!strcmp(cmd_pre,"pwd")) cmd_int=pwd;
if(!strcmp(cmd_pre,"quit")) cmd_int=quit;
if(!strcmp(cmd,"lls")) cmd_int=lls;
if(strstr(cmd,"lcd")!=NULL) cmd_int=lcd;
if(strstr(cmd_pre,"cd")!=NULL) cmd_int=cd;
if(strstr(cmd_pre,"put")!=NULL) cmd_int=put;
if(strstr(cmd_pre,"get")!=NULL) cmd_int=get;
//handle cmd
switch(cmd_int){
case ls:
case pwd:
file=popen(cmd,"r");
fread(msg.data,sizeof(msg.data),1,file);
printf("%s",msg.data);
write(ss_fd,&msg,sizeof(msg));
break;
case cd:
p=strtok(cmd," ");
p=strtok(NULL," ");
printf("dir:%s \r\n",p);
strcpy(msg.data,"change dir over \r\n");
write(ss_fd,&msg,sizeof(msg)); //must give a msg back
chdir(p);
break;
case get:
p=strtok(cmd," ");
p=strtok(NULL," ");
printf("dir:%s \r\n",p);
if(!access(p,F_OK)){ //if have this file
msg.flag=dofile;
fd=open(p,O_RDONLY);
read(fd,&msg.data,sizeof(msg.data));
send(ss_fd,&msg,sizeof(msg),0);
close(fd);
}else{ //if no this file
send(ss_fd,"no this file \r\n",16,0);
}
break;
case put:
p=strtok(cmd," ");
p=strtok(NULL," ");
printf("dir:%s \r\n",p);
fd=open(p,O_RDWR|O_CREAT,0666);
write(fd,&msg.data,strlen(msg.data));
close(fd);
strcpy(msg.data,"i have got a file.\r\n");
write(ss_fd,&msg,sizeof(msg)); //must give a msg back
break;
case quit:
msg.flag=quit;
printf("quit==========\r\n");
exit(-1);
}
}
int main(void)
{
int s_fd,nread,len;
struct sockaddr_in s_ddr; //build server msg
struct sockaddr_in c_ddr; //save clinet msg
s_fd= socket(AF_INET, SOCK_STREAM, 0);//1.build a soket specified
if(s_fd==-1){
perror("error is");
}
//2.build all bind
s_ddr.sin_family=AF_INET;
s_ddr.sin_port=htons(8880);
s_ddr.sin_addr.s_addr=htonl(INADDR_ANY);
//give the bind
bind(s_fd,(struct sockaddr *)&s_ddr,sizeof(s_ddr));
//3.waite for client
listen(s_fd,8);
//4.accept come and connect for once
len=sizeof(c_ddr);
while(1){
ss_fd=accept(s_fd,(struct sockaddr *)&c_ddr,&len);
if(ss_fd == -1){
perror("accept:");
}
printf("conect succese!==========\r\n");
//5.read from connect ss_fd
if(fork()==0){ //creat kid pid for handler msg and cmd
//5.2 read
while(1){
memset(&msg,'\0',sizeof(msg)); //clear msg
printf("====================\r\n");
nread=read(ss_fd,&msg,sizeof(msg));
if(nread==0){ // cannot recv cmd
printf("connect is cutdon! \r\n");
break;
}else{ //can recive cmd
printf("server receved cmd:%s \r\n",msg.cmd);
printf("====================\r\n");
handler_cmd(msg.cmd); //hander the cmd give msg data
}
}
}
}
close(ss_fd);
close(s_fd);
return 0;
}
3.2客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int cmd_to_number(char *cmd)
{
if(!strcmp(cmd,"ls")) return ls;
if(!strcmp(cmd,"pwd")) return pwd;
if(!strcmp(cmd,"quit")) return quit;
if(!strcmp(cmd,"lls")) return lls;
if(strstr(cmd,"lcd")!=NULL) return lcd;
if(strstr(cmd,"cd")!=NULL) return cd;
if(strstr(cmd,"put")!=NULL) return put;
if(strstr(cmd,"get")!=NULL) return get;
return -1;
}
int handler_cmd(struct Msg msg, int s_fd)
{
//handle cmd
int fd,n_read;
char p_pre[12];
char *p=NULL;
int ret=cmd_to_number(msg.cmd);
switch(ret){
case ls:
case pwd:
case cd:
send(s_fd,&msg,sizeof(msg),0);
break;
case get:
send(s_fd,&msg,sizeof(msg),0);
break;
case put:
strcpy(p_pre,msg.cmd);
printf("dir:%s \r\n",p_pre);
p=strtok(p_pre," ");
p=strtok(NULL," "); //get file_name
printf("dir:%s \r\n",p);
if(!access(p,F_OK)){ //if have this file
fd=open(p,O_RDONLY);
n_read=read(fd,&msg.data,1024);
if(n_read>=1024){
printf("file over 1024! faild! \r\n");
}
send(s_fd,&msg,sizeof(msg),0);
memset(&msg.data,0,1024);
printf("client put cmd is: %s \r\n",msg.cmd);
close(fd);
}else{ //if no this file
send(s_fd,"no this file",16,0);
}
break;
case lls:
system("ls");
break;
case lcd:
strcpy(p_pre,msg.cmd);
p=strtok(p_pre," ");
p=strtok(NULL," "); //get file_
printf("dir:%s \r\n",p);
chdir(p);
break;
case quit:
send(s_fd,&msg,sizeof(msg),0);
close(s_fd);
exit(-1);
}
return ret;
}
void handle_server_msg(struct Msg msg,int s_fd)
{
int n_read,fd;
char *p;
struct Msg recv_msg; //this is new msg use it becase need pre msg cmd
n_read=read(s_fd,&recv_msg,sizeof(recv_msg)); //while block
if(n_read==0){
printf("\r\n server is outline! ");
exit(-1);
}
if(recv_msg.flag == dofile){
p=strtok(msg.cmd," ");
p=strtok(NULL," "); //get file_name
fd=open(p,O_RDWR|O_CREAT,0666);
printf("creat the file\r\n");
write(fd,&recv_msg.data,strlen(recv_msg.data));
close(fd);
putchar('>');
fflush(stdout);
}else{ //normal direct cmd
printf("-------------------->\r\n");
printf("%s >",recv_msg.data);
printf("--------------------");
putchar('>');
fflush(stdout);
}
}
int main(int argc,char *argv[])
{
int flag,s_fd,n_read,ret;
struct sockaddr_in c_ddr;
struct Msg msg;
//1.build socket
s_fd=socket(AF_INET,SOCK_STREAM,0);
//2.0 prepare server addr
memset(&c_ddr,0,sizeof(c_ddr)); //clear c_ddr
c_ddr.sin_family=AF_INET;
c_ddr.sin_port=htons(8880);
inet_aton("127.0.0.1",&c_ddr.sin_addr);
//2.connect server get s_fd
if(connect(s_fd,(struct sockaddr *)&c_ddr,sizeof(c_ddr))==-1){
perror("error");
exit(-1);
}
printf("connect success==============\r\n");
while(1){
//1.get cmd form keyborad and handle cmd to server
memset(&msg,0,sizeof(msg));
printf("\r\n >");
gets(msg.cmd);
int ret=handler_cmd(msg,s_fd);
//2.handle the msg form server
if(ret==-1){
printf("no this cmd!\r\n ");
fflush(stdout);
continue;
}
if(ret==5 || ret==6){
printf("client cmd!\r\n ");
fflush(stdout);
continue;
}
handle_server_msg(msg,s_fd);
}
close(s_fd);
return 0;
}
4.问题总结
- 在put 发送文件中,当文件大小超过给定字节就会溢出,导致整个结构体崩坏,破坏cmd。
思考:除了加大给定字节大小,还有其它办法吗?strcpy是怎么实现的
read函数陷阱:read函数读的字节小于fd文件字节,会崩坏buf里的数据。 - strtok函数会破坏使用的字符串,要保留原来字符串需要strcpy复制出来处理。
- 本地命令不用服务器处理的命令,ret=5,ret=6不要进入hanle_server_msg用recv函数阻塞