开发平台:Linux
开发工具:Ubuntu, sourceInsight4.0
项目介绍: 本项目基于TCP/IP协议创建一个网络通信系统,可以实现客户之间的聊天通信以及文件传输,同时利用进程实现多客户群聊,多个客户也可同时从服务器下载文件实现文件共享,客户可向服务器发送ls命令获取服务器端的文件目录,并发送get+filename获取文件,也可发送put+filename上传文件到服务器。
protocol.h
#ifndef __PROTOCOL_H__
#define __PROTOCOL_H__
#define FTP_ROOT_DIR "/home/gec/tftp"
//命令号, 命令参数
//命令号-> 整数
enum CMD_NO
{
FTP_CMD_LS = 1024, //
FTP_CMD_GET,
FTP_CMD_PUT,
FTP_CMD_BYE,
FTP_CMD_NUM // 命令个数
};
//出错码
enum resp_result
{
RESP_SUCCESS = 0, //成功
RESP_PACK_ERROR, //失败,包的长度不对
RESP_PACK_NOEND, //包没有结束,可以再次收包
RESP_PACK_NOFILE //没有可以获取的文件
};
//参数: 参数长度, 参数内容
/*
0xc0 : 包头
pkg_len ;//4bytes, 小端模式,整个数据包的长度
4(pkg_len) + 4 (cmd_no) + arg_1 +...
cmd_no // 4bytes, 小端模式
arg_1;
arg_1_len; //4bytes , 小端模式
arg_1_data; len长度
arg_2:
arg_2_len; //4bytes,小端模式
arg_2_data;
....
0xC0:包尾
*/
// cmd: ls
//0xc0 ___包长度______ __命令号()__ 0xc0
// 0xc0 0x80 0x00 0x00 0x00
//cmd: get
//0xc0 ____包长度(4)___ ___命令号(4)___ ___arg_1_Len(4)_ ___filename(r)___ 0xc0
#if 0
unsigned char cmd[1024];
int i = 0;
int pkg_len = 1024;
cmd[0] = 0xc0;
cmd[1] = pkg_len & 0xff;
cmd[2] = ( pkg_len >> 8) & 0xff;
cmd[3] = (pkg_len >> 16) & 0xff;
cmd[4] = (pkg_len >> 24) & 0xff;
#endif
//CMD_RESP
/*
0xc0: 包头
pkg_len: 整个数据包的长度,4bytes, 小端模式
cmd_no : 命令号,4bytes, 小端模式,表示回复哪条命令
resp_len: 回复内容的长度,4bytes, 小端模式
result + resp_conent
1 + x
result: 执行成功或失败,1 bytes , 0表示成功,其他表示失败
resp_conent: 回复内容
成功:
ls: 文件名字 各文件名之间用空格分隔
get: 文件大小,4bytes, 小端模式
失败:
出错码
0xc0:包尾
*/
//ls 的回复
// 0xc0 --pkg_Len(4)--- ---cmd_No(4)--- --resp_len(4)-- --result(1)-- --filenames(r, 名字以空格分开)--- 0xc0
//get 的回复
//0xc0 ---pkg_len(4)--- ----cmd_no(4)--- ---resp_len(4)--- ---result(1)-- --file_size(4, 小端模式)-- 0xc0
#endif
tcp_client.c
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "protocol.h"
int connect_server(char *ip,int port)
{
/**1.创建套接字**/
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == sockfd)
{
perror("socket error");
return -1;
}
/**2.发起链接请求**/
int ret;
struct sockaddr_in sAddr;
memset(&sAddr,0,sizeof(sAddr));
sAddr.sin_family = AF_INET;
sAddr.sin_port = htons(port);
sAddr.sin_addr.s_addr = inet_addr(ip);
ret = connect(sockfd,(struct sockaddr*)&sAddr,sizeof(sAddr));
if(-1 ==ret)
{
perror("conncet error");
return -1;
}
//
return sockfd;
}
void send_cmd(int sockfd,unsigned char *cmd,int len)
{
//
int ret = send(sockfd,cmd,len,0);
if(-1 == ret)
{
perror("send cmd error");
return;
}
//
}
void recv_ls_val(int sockfd)
{
/**
服务器回复数据,事先会回复一个数据包
0xc0 L E N S E R R N S I Z E 0xc0
回复数据包长度 回复的验证信息 后续正文大小
紧接着服务器会回复正文
size个字节的数据
**/
int i=0;
unsigned char ch = 0;
unsigned char cmd[500] = {0};
/*************************************接收回复数据包*******************************************************/
//命令以0xc0开头,所以如果收到的不是0xc0则舍弃不要
do
{
//
read(sockfd,&ch,1);
perror("read ls");
}while(ch != 0xc0);
//
//排除连续的0xc0
while(ch == 0xc0)
{
read(sockfd,&ch,1);
}//确保读取的是数据包的第一个字节
//
while(ch != 0xc0)//读到的是数据包的内容,读到包尾结束
{
cmd[i++] = ch;
read(sockfd,&ch,1);
}
/***************************************数据包接收完成*****************************************************/
/******************************************解析数据包******************************************************/
//解析包长
int cmd_len = cmd[0] | cmd[1]<<8 | cmd[2]<<16 | cmd[3]<<24;
if(cmd_len != i)
{
printf("read value error,the pack len is no right\n");
//send_error(connfd);
return;
}
//解析验证信息
int err_no = cmd[4] | cmd[5]<<8 | cmd[6]<<16 | cmd[7]<<24;
if(err_no == RESP_PACK_ERROR)
{
printf("cmd error\n");
return;
}
int data_len = cmd[8] | cmd[9]<<8 | cmd[10]<<16 | cmd[11]<<24;
printf("recv data len:%d\n",data_len);
/*****************************************解析完毕**********************************************************/
/****************************************接收回复正文*******************************************************/
memset(cmd,0,300);
printf("data_len %d\n",data_len);
recv(sockfd,cmd,data_len,0);
printf("recv data is:%s\n",cmd);
}
void send_ls(int sockfd)
{
/*
ls 命令的数据包:
0xc0 L E N S C M D S 0xc0
4个字节表示包的长度 4个字节表示命令
*/
int i = 0;
int pak_len = 4+4;
unsigned char cmd[50] ={0};
/**********************************组合数据包***************************************************/
/**帧头**/
cmd[i++] = 0xc0;
/**包长**/
cmd[i++] = pak_len & 0xff;
cmd[i++] = pak_len>>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/**命令**/
cmd[i++] = FTP_CMD_LS & 0xff;
cmd[i++] = FTP_CMD_LS>>8 & 0xff;
cmd[i++] = FTP_CMD_LS>>16 & 0xff;
cmd[i++] = FTP_CMD_LS>>24 & 0xff;
/**帧尾**/
cmd[i++] = 0xc0;
/******************************数据包组合完毕***************************************************/
/**发送命令**/
send_cmd(sockfd,cmd,i); //将数据包发送出去后,等待服务器的处理结果
/**接收返回结果**/
recv_ls_val(sockfd);
}
void recv_get_val(int sockfd,int fd)
{
/**
服务器回复数据,事先会回复一个数据包
0xc0 L E N S E R R N S I Z E 0xc0
回复数据包长度 回复的验证信息 后续正文大小
紧接着服务器会回复正文
size个字节的数据
**/
int i=0;
unsigned char ch = 0;
unsigned char cmd[300] = {0};
/*************************************接收回复数据包*******************************************************/
//命令以0xc0开头,所以如果收到的不是0xc0则舍弃不要
do
{
//
read(sockfd,&ch,1);
}while(ch != 0xc0);
//排除连续的0xc0
while(ch == 0xc0)
{
read(sockfd,&ch,1);
}//确保读取的是数据包的第一个字节
while(ch != 0xc0)//读到的是数据包的内容,读到包尾结束
{
cmd[i++] = ch;
read(sockfd,&ch,1);
}
/***************************************数据包接收完成*****************************************************/
/******************************************解析数据包******************************************************/
//解析包长
int cmd_len = cmd[0] | cmd[1]<<8 | cmd[2]<<16 | cmd[3]<<24;
if(cmd_len != i)
{
printf("read value error,the pack len is no right\n");
//send_error(connfd);
return;
}
//解析验证信息
int err_no = cmd[4] | cmd[5]<<8 | cmd[6]<<16 | cmd[7]<<24;
if(err_no == RESP_PACK_ERROR)
{
printf("cmd error\n");
return;
}
int data_len = cmd[8] | cmd[9]<<8 | cmd[10]<<16 | cmd[11]<<24;
printf("recv data len:%d\n",data_len);
/*****************************************解析完毕**********************************************************/
/*****************************************接收文件数据******************************************************/
unsigned char *p = (unsigned char *)malloc(data_len+1);
int ret=0;
while(1)
{
ret += read(sockfd,p+ret,data_len);
if(ret == data_len)
{
break;
}
else if(ret == -1 || ret == 0)
{
perror("read error");
return;
}
}
write(fd,p,data_len);//将正文写入本地文件
/********************************************判断文件是否发送完成*******************************************************/
if(err_no == RESP_PACK_NOEND) //文件内容没有发完,会在后续发送
{
recv_get_val(sockfd,fd); //再次接收后续包
}
}
void send_get(int sockfd,char *file)
{
/**
get命令包格式:
0xc0 L E N S C M D S S I Z E A R G S . . . . 0xc0
包长(4bytes) 命令号(6bytes) 参数长度(4bytes) 参数正文(size bytes)
**/
int arg_len = strlen(file);
int pak_len = 4 + 4 + 4 + arg_len;
unsigned char cmd[50] = {0}; //保存命令数据包
int i=0;
/****************************************封装命令包****************************************************/
/*包头*/
cmd[i++] = 0Xc0;
/*包长 小端模式*/
cmd[i++] = pak_len & 0xff;
cmd[i++] = pak_len>>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/*命令号 小端模式*/
cmd[i++] = FTP_CMD_GET & 0xff;
cmd[i++] = FTP_CMD_GET>>8 & 0xff;
cmd[i++] = FTP_CMD_GET>>16 & 0xff;
cmd[i++] = FTP_CMD_GET>>24 & 0xff;
/*参数长度*/
cmd[i++] = arg_len & 0xff;
cmd[i++] = arg_len>>8 & 0xff;
cmd[i++] = arg_len>>16 & 0xff;
cmd[i++] = arg_len>>24 & 0xff;
/*参数正文*/
int j=0;
for(j=0;j>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/*回复码*/
if(ret < file_len) //文件没完
{
cmd[i++] = RESP_PACK_NOEND & 0xff;
cmd[i++] = RESP_PACK_NOEND>>8 & 0xff;
cmd[i++] = RESP_PACK_NOEND>>16 & 0xff;
cmd[i++] = RESP_PACK_NOEND>>24 & 0xff;
}
else
{
cmd[i++] = RESP_SUCCESS & 0xff;
cmd[i++] = RESP_SUCCESS>>8 & 0xff;
cmd[i++] = RESP_SUCCESS>>16 & 0xff;
cmd[i++] = RESP_SUCCESS>>24 & 0xff;
}
/*正文长度*/
cmd[i++] = size & 0xff;
cmd[i++] = size>>8 & 0xff;
cmd[i++] = size>>16 & 0xff;
cmd[i++] = size>>24 & 0xff;
/*帧尾*/
cmd[i++] = 0xc0;
send_cmd(sockfd,cmd,i); //回复数据包
send(sockfd,buf,size,0); //回复正文
}
}
void send_put(int sockfd,char *file)
{
/**
get命令包格式:
0xc0 L E N S C M D S S I Z E A R G S . . . . 0xc0
包长(4bytes) 命令号(6bytes) 参数长度(4bytes) 参数正文(size bytes)
**/
int arg_len = strlen(file);
int pak_len = 4 + 4 + 4 + arg_len;
unsigned char cmd[50] = {0}; //保存命令数据包
int i=0;
/****************************************封装命令包****************************************************/
/*包头*/
cmd[i++] = 0Xc0;
/*包长 小端模式*/
cmd[i++] = pak_len & 0xff;
cmd[i++] = pak_len>>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/*命令号 小端模式*/
cmd[i++] = FTP_CMD_PUT & 0xff;
cmd[i++] = FTP_CMD_PUT>>8 & 0xff;
cmd[i++] = FTP_CMD_PUT>>16 & 0xff;
cmd[i++] = FTP_CMD_PUT>>24 & 0xff;
/*参数长度*/
cmd[i++] = arg_len & 0xff;
cmd[i++] = arg_len>>8 & 0xff;
cmd[i++] = arg_len>>16 & 0xff;
cmd[i++] = arg_len>>24 & 0xff;
/*参数正文*/
int j=0;
for(j=0;j
tcp_server.c
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "protocol.h"
#define FTP_DIR "/home/gec/tftp"
void send_pack(int connfd,unsigned char *cmd,int len)
{
//
printf("%d %x\n",connfd,cmd[0]);
int ret = send(connfd,cmd,len,0);
if(-1 == ret)
{
perror("send cmd error");
return;
}
//
}
void send_error(int connfd)
{
int i = 0;
int pak_len = 4+4;
unsigned char cmd[50] ={0};
/**帧头**/
cmd[i++] = 0xc0;
/**包长**/
cmd[i++] = pak_len & 0xff;
cmd[i++] = pak_len>>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/**错误码**/
cmd[i++] = RESP_PACK_ERROR & 0xff;
cmd[i++] = RESP_PACK_ERROR>>8 & 0xff;
cmd[i++] = RESP_PACK_ERROR>>16 & 0xff;
cmd[i++] = RESP_PACK_ERROR>>24 & 0xff;
/**帧尾**/
cmd[i++] = 0xc0;
/**发送回复数据**/
send_pack(connfd,cmd,i);
}
void repa_ls(int connfd)
{
int i = 0;
int pak_len = 0;
unsigned char file[500] = {0};
unsigned char cmd[500] = {0};
DIR *dirp = opendir(FTP_DIR);
if(NULL == dirp)
{
perror("open dir error");
return ;
}
//
struct dirent *dir;
while((dir = readdir(dirp)) != NULL)
{
sprintf(file+strlen(file),"%s ",dir->d_name);
printf("%d %s\n",strlen(file),dir->d_name);
}
//
/**帧头**/
cmd[i++] = 0xc0;
/**包长**/
pak_len = 4 + 4 + 4;
cmd[i++] = pak_len & 0xff;
cmd[i++] = pak_len>>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/**错误码**/
cmd[i++] = RESP_SUCCESS & 0xff;
cmd[i++] = RESP_SUCCESS>>8 & 0xff;
cmd[i++] = RESP_SUCCESS>>16 & 0xff;
cmd[i++] = RESP_SUCCESS>>24 & 0xff;
/**回复数据长度**/
cmd[i++] = strlen(file) & 0xff;
cmd[i++] = strlen(file)>>8 & 0xff;
cmd[i++] = strlen(file)>>16 & 0xff;
cmd[i++] = strlen(file)>>24 & 0xff;
printf("recv ls data size %d\n",strlen(file));
printf("%s\n",file);
/**帧尾**/
cmd[i++] = 0xc0;
send_pack(connfd,cmd,i); //回复命令的处理结果
send_pack(connfd,file,strlen(file));//回复命令数据
//
}
void send_file_data(int connfd,int fd,int file_len)
{
/*
L E N S E R R N S I Z E
*/
unsigned char cmd[50] = {0};
unsigned char buf[256] = {0};
int pak_len;
int size;
int i;
int ret = 0;
while(1)
{
size = read(fd,buf,256);
if(size == 0 || size==-1)
{
perror("read error");
break;
}
pak_len = 4 + 4 + 4;
ret += size;
/*帧头*/
i = 0;
cmd[i++] = 0xc0;
/*包长 小端模式*/
cmd[i++] = pak_len & 0xff;
cmd[i++] = pak_len>>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/*回复码*/
if(ret < file_len) //文件没完
{
cmd[i++] = RESP_PACK_NOEND & 0xff;
cmd[i++] = RESP_PACK_NOEND>>8 & 0xff;
cmd[i++] = RESP_PACK_NOEND>>16 & 0xff;
cmd[i++] = RESP_PACK_NOEND>>24 & 0xff;
}
else
{
cmd[i++] = RESP_SUCCESS & 0xff;
cmd[i++] = RESP_SUCCESS>>8 & 0xff;
cmd[i++] = RESP_SUCCESS>>16 & 0xff;
cmd[i++] = RESP_SUCCESS>>24 & 0xff;
}
/*正文长度*/
cmd[i++] = size & 0xff;
cmd[i++] = size>>8 & 0xff;
cmd[i++] = size>>16 & 0xff;
cmd[i++] = size>>24 & 0xff;
/*帧尾*/
cmd[i++] = 0xc0;
send_pack(connfd,cmd,i); //回复数据包
send(connfd,buf,size,0); //回复正文
}
}
void repa_get(int connfd,unsigned char *cmd)
{
/************************************解析get参数*******************************************************/
int i = 8;
int arg_len = cmd[8] | cmd[9]<<8 | cmd[10]<<16 | cmd[11]<<24;
char *file = (char *)malloc(arg_len+1);
memset(file,0,arg_len+1);
for(i=0;i>8 & 0xff;
cmd[i++] = pak_len>>16 & 0xff;
cmd[i++] = pak_len>>24 & 0xff;
/*回复码*/
cmd[i++] = RESP_PACK_NOFILE & 0xff;
cmd[i++] = RESP_PACK_NOFILE>>8 & 0xff;
cmd[i++] = RESP_PACK_NOFILE>>16 & 0xff;
cmd[i++] = RESP_PACK_NOFILE>>24 & 0xff;
/*帧尾*/
cmd[i++] = 0xc0;
send_pack(connfd,cmd,i);
}
else
{
int file_len = lseek(fd,0,SEEK_END); //获取文件大小
lseek(fd,0,SEEK_SET);
send_file_data(connfd,fd,file_len); //回复文件内容
close(fd);
}
}
void recv_get_val1(int connfd,int fd)
{
/**
服务器回复数据,事先会回复一个数据包
0xc0 L E N S E R R N S I Z E 0xc0
回复数据包长度 回复的验证信息 后续正文大小
紧接着服务器会回复正文
size个字节的数据
**/
int i=0;
unsigned char ch = 0;
unsigned char cmd[300] = {0};
/*************************************接收回复数据包*******************************************************/
//命令以0xc0开头,所以如果收到的不是0xc0则舍弃不要
do
{
//
read(connfd,&ch,1);
}while(ch != 0xc0);
//排除连续的0xc0
while(ch == 0xc0)
{
read(connfd,&ch,1);
}//确保读取的是数据包的第一个字节
while(ch != 0xc0)//读到的是数据包的内容,读到包尾结束
{
cmd[i++] = ch;
read(connfd,&ch,1);
}
/***************************************数据包接收完成*****************************************************/
/******************************************解析数据包******************************************************/
//解析包长
int cmd_len = cmd[0] | cmd[1]<<8 | cmd[2]<<16 | cmd[3]<<24;
if(cmd_len != i)
{
printf("read value error,the pack len is no right\n");
//send_error(connfd);
return;
}
//解析验证信息
int err_no = cmd[4] | cmd[5]<<8 | cmd[6]<<16 | cmd[7]<<24;
if(err_no == RESP_PACK_ERROR)
{
printf("cmd error\n");
return;
}
int data_len = cmd[8] | cmd[9]<<8 | cmd[10]<<16 | cmd[11]<<24;
printf("recv data len:%d\n",data_len);
/*****************************************解析完毕**********************************************************/
/*****************************************接收文件数据******************************************************/
unsigned char *p = (unsigned char *)malloc(data_len+1);
int ret=0;
while(1)
{
ret += read(connfd,p+ret,data_len);
if(ret == data_len)
{
break;
}
else if(ret == -1 || ret == 0)
{
perror("read error");
return;
}
}
write(fd,p,data_len);//将正文写入本地文件
/********************************************判断文件是否发送完成*******************************************************/
if(err_no == RESP_PACK_NOEND) //文件内容没有发完,会在后续发送
{
recv_get_val1(connfd,fd); //再次接收后续包
}
}
void repa_put(int connfd,unsigned char *cmd)
{
int i = 8;
int arg_len = cmd[8] | cmd[9]<<8 | cmd[10]<<16 | cmd[11]<<24;
char *file = (char *)malloc(arg_len+1);
memset(file,0,arg_len+1);
for(i=0;i 0)
{
close(connfd);
continue;
}
else if(pid == 0)
{
//
recv_cmd(connfd);
exit(0);
}
}
/**7.处理并退出**/
close(sockfd);
close(connfd);
return 0;
}