文章目录
前言
本文章是前段时间学习linux编程的一个小总结,如果不总结的话我怕我学完就忘emmm(其实已经忘的差不多了),本文是跟着上官可编程老师与朱有鹏老师学习之后的练手作品,本人学疏才浅,暂时只能到这个地步
提示:以下是本篇文章正文内容
一、实现的功能
利用socket,建立起服务端与客户端的对接;(服务端能支持多台客户端的同时连接)
1.客户端
1、客户端输入ls指令,能获取服务端上文件列表。
2、客户端输入cd指令+路径,可以切换服务端的目录。
3、在程序运行的过程中,客户端输入lls指令 能够查看自己的文件列表
4、在程序运行的过程中,客户端输入lcd指令+路径 能够切换自己的目录。
5、客户端输入get指令+文件名,能将服务端上面的某个文件下载到客户端
6、客户端输入put指令+文件名,能将客户端上面的某个文件上传到服务端
2.服务端
1、不断监听客户端的指令(等待指令)。
2、在接收上面客户端的指令后,去执行指令。
二、实现思路
1.建立socket连接
(1)服务端建立socket连接:socket—>blind—>listen—>accept
(2)客户端建立socket连接:socket—>connect
2.客户端和服务端进行信息的交互
(1)定义指令
(2)客户端匹配用户输入的指令,客户端发送指令到服务端,服务端解析指令作出响应
三、具体流程分析(代码)
第一步:建立socket连接
服务端建立socket连接
流程:socket—>blind—>listen—>accept
代码如下(示例):
int main(int argc,char **argv)
{
int c_fd;
int s_fd;
int n_read;
char readBuf[128];
struct Msg msg;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
/*调试信息,这里我想要用argv,argc,进行手动指定端口号和IP地址,所以在用这两个参数之前需要先判断*/
if(argc != 3){
printf("parameter error!\n");
exit(-1);
}
/*用之前清空,防止数据产生混乱*/
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
/*s_fd:这里socket返回的是监听fd,是用来监听客户端的,不能用来和任何客户端进行读写*/
/*AF_INET:IPv4,SOCK_STREAM:tcp协议,0:使用系统默认的方式*/
s_fd = socket(AF_INET,SOCK_STREAM,0);
/*如果返回值为-1说明错误,打印出错误号*/
if(s_fd == -1){
perror("socket");
exit(-1);
}
//2.bind
/*有IPv4和IPv6两种,这里我们选IPv4*/
s_addr.sin_family = AF_INET; // 设置地址族为IPv4
/*htons:端口号大小端的转换(具体大小端要看对应的计算机)
atoi:字符串转换成整型,使我们输入的数字端口号可以被识别
*/
s_addr.sin_port = htons(atoi(argv[2])); // 设置地址的端口号信息
/*inet_aton:将我们输入的地址字符串转换成网络可以识别的api*/
inet_aton(argv[1],&s_addr.sin_addr); // 设置IP地址
/*(struct sockaddr *)&s_addr这之所以需要类型转换的原因是因为我们用的是IPV4的结构体,而给出的标准是IPV4和IPV6的通用结构体,所以我们一般用IPV4的结构体之后将其转换为系统给出的通用结构体,其实转不转都一样,就是会报警告而已,而这个警告是我们可以理解的警告,所以问题不大*/
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
/*设置最大监听10个*/
listen(s_fd,10);
//4.accept
int clen = sizeof(struct sockaddr_in);
/*while不停的循环接收客户端发来的信息,并且作出响应*/
while(1){
/*accept返回的fd叫做连接fd,用来和连接那端的客户端程序进行读写。*/
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));
/*当我的accept每接收到一个信息就建立一个子进程*/
if(fork() == 0){
while(1){
/*每次循环接收信息之前,清空之前的信息*/
memset(msg.cmd,0,sizeof(msg.cmd));
/*用accept建立的连接fd来读写信息*/
n_read = read(c_fd,&msg,sizeof(msg));
if(n_read == 0){
printf("client out\n");
break;
}else if(n_read > 0){
/*一旦发现有信息,就执行这个函数*/
msg_handler(msg,c_fd);
}
}
}
}
/*关闭监听fd和连接fd*/
close(c_fd);
close(s_fd);
return 0;
}
客户端建立socket连接
流程:socket—>connect
客户端建立socket与服务端建立socket大同小异这里就不过多赘述了
代码如下(示例):
int main(int argc,char **argv)
{
int c_fd;
struct Msg msg;
struct sockaddr_in c_addr;
if(argc != 3){
printf("parameter error!\n");
exit(-1);
}
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1){
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
//2.connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
perror("connect");
exit(-1);
}
printf("connect ...\n");
return 0;
}
第二步:客户端和服务端进行信息的交互
定义指令
共有指令
代码如下(示例):
#define LS 0
#define PWD 1
#define GET 2
#define IFGO 3
#define CD 4
#define PUT 5
#define LLS 6
#define LCD 7
#define LPWD 8
#define QUIT 9
#define DOFILE 10
struct Msg
{
int type;
char cmd[1024];
char buf[128];
};
服务端指令
代码如下(示例):
int get_cmd_type(char *cmd) //检测命令并转为相应int
{
if(!strcmp("ls",cmd))