开发平台: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 <sys/types.h> /* See NOTES */
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#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);
/*****************************************解析完毕**********************************************************/
/****************************************接收回复正文*******************