使用c语言socket通信写了一个客户端与服务器,然后客户端向服务器发送文件,服务器接收。使用自己定义的数据包协议,服务器使用单线程epoll。最后测试时,文本文件发送没有问题,但是发送图片文件,最后图片的大小没有问题,但是无法打开,提示错误:分析 JPEG 图像文件时出错(Improper call to JPEG library in state 201) 。
文件的操作使用linux下系统调用read,write,open 。请问问题可能出现在哪里?
服务器代码:
#include<iostream>
#include<cstring>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/signal.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
using namespace std;
#define NET_PACKET_DATA_SIZE 5000
#define PORT 6666 //服务器端口
#define LISTEN_SIZE 1023 //连接请求队列的最大长度
#define EPOLL_SIZE 1023 //epoll监听客户端的最大数目
int ss=0;///////////
/// 网络数据包包头
struct NetPacketHeader
{
unsigned short wDataSize; ///< 数据包大小,包含封包头和封包数据大小
unsigned short wOpcode; ///< 操作码
};
/// 网络数据包
struct NetPacket
{
NetPacketHeader Header; ///< 包头
unsigned char Data[NET_PACKET_DATA_SIZE]; ///< 数据
};
/// 网络操作码
enum eNetOpcode
{
NET_TEST1 = 1,
};
struct File_message
{
char filename[100]; //文件名
long filesize; //文件大小
};
struct File_data
{
char filename[100]; //文件名
char buffer[1024]; //文件内容
};
void my_err(const char *err_string,int line) //自定义错误函数
{
cerr<<"line:"<<line<<endl; //输出错误发生在第几行
perror(err_string); //输出错误信息提示
exit(1);
}
bool dealwithpacket(int conn_fd,char *recv_data,uint16_t wOpcode,int datasize) //处理接收到的数据
{
cout<<++ss<<":"<<"datasize"<<datasize<<endl;///////////////////
int fd;
if(wOpcode==1) //接收文件信息
{
File_message *file_message=(File_message*)recv_data;
strcat(file_message->filename,"(2)");
if((fd=open(file_message->filename,O_RDWR|O_APPEND|O_CREAT,0777))<0)
{
cout<<"创建文件失败"<<endl;
return false;
}
}
else if(wOpcode==2) //接收文件内容
{
File_data * file_data=(File_data*)recv_data;
strcat(file_data->filename,"(2)");
if((fd=open(file_data->filename,O_RDWR|O_APPEND))<0)
{
cout<<"打开文件失败"<<endl;
return false;
}
if(write(fd,file_data->buffer,datasize-sizeof(file_data->filename))<0)
{
cout<<"写入文件失败"<<endl;
return false;
}
close(fd);
}
return true;
}
bool server_recv(int conn_fd) //接收数据函数
{
int nrecvsize=0; //一次接收到的数据大小
int sum_recvsize=0; //总共收到的数据大小
int packersize; //数据包总大小
int datasize; //数据总大小
char recv_buffer[10000]; //接收数据的buffer
memset(recv_buffer,0,sizeof(recv_buffer)); //初始化接收buffer
while(sum_recvsize!=sizeof(NetPacketHeader))
{
nrecvsize=recv(conn_fd,recv_buffer+sum_recvsize,sizeof(NetPacketHeader)-sum_recvsize,0);
if(nrecvsize==0)
{
cout<<"从客户端接收数据失败"<<endl;
return false;
}
sum_recvsize+=nrecvsize;
}
NetPacketHeader *phead=(NetPacketHeader*)recv_buffer;
packersize=phead->wDataSize; //数据包大小
datasize=packersize-sizeof(NetPacketHeader); //数据总大小
while(sum_recvsize!=packersize)
{
nrecvsize=recv(conn_fd,recv_buffer+sum_recvsize,packersize-sum_recvsize,0);
if(nrecvsize==0)
{
cout<<"从客户端接收数据失败"<<endl;
return false;
}
sum_recvsize+=nrecvsize;
}
dealwithpacket(conn_fd,(char*)(phead+1),phead->wOpcode,datasize);
}
int main()
{
int sock_fd; //监听套接字
int conn_fd; //连接套接字
int epollfd; //epoll监听描述符
socklen_t cli_len; //记录连接套接字地址的大小
struct epoll_event event; //epoll监听事件
struct epoll_event* events; //epoll监听事件集合
struct sockaddr_in cli_addr; //客户端地址
struct sockaddr_in serv_addr; //服务器地址
//创建一个套接字
sock_fd=socket(AF_INET,SOCK_STREAM,0);
if(sock_fd<0)
{
my_err("socket",__LINE__);
}
//设置该套接字使之可以重新绑定端口
int optval=1;
if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,(void*)&optval,sizeof(int))<0)
{
my_err("setsock",__LINE__);
}
//初始化服务器端地址结构
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(PORT);
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(sock_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in))<0)
{
my_err("bind",__LINE__);
}
//将套接字转化为监听套接字
if(listen(sock_fd,LISTEN_SIZE)<0)
{
my_err("listen",__LINE__);
}
cli_len=sizeof(struct sockaddr_in);
events=(struct epoll_event*)malloc(sizeof(struct epoll_event)*EPOLL_SIZE); //分配内存空间
//创建一个监听描述符epoll,并将监听套接字加入监听列表
epollfd=epoll_create(EPOLL_SIZE);
if(epollfd==-1)
{
my_err("epollfd",__LINE__);
}
event.events = EPOLLIN;
event.data.fd = sock_fd;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,sock_fd,&event)<0)
{
my_err("epoll_ctl",__LINE__);
}
while(1) //循环监听事件
{
int sum=0,i;
sum=epoll_wait(epollfd,events,EPOLL_SIZE,-1);
for(i=0;i<sum;i++)
{
if(events[i].data.fd==sock_fd) //客户端请求连接
{
conn_fd=accept(sock_fd,(struct sockaddr*)&cli_addr,&cli_len);
if(conn_fd<0)
{
my_err("accept",__LINE__);
}
event.events = EPOLLIN | EPOLLRDHUP; //监听连接套接字的可读和退出
event.data.fd = conn_fd;
if(epoll_ctl(epollfd,EPOLL_CTL_ADD,conn_fd,&event)<0) //将新连接的套接字加入监听
{
my_err("epoll",__LINE__);
}
cout<<"a connet is connected,ip is "<<inet_ntoa(cli_addr.sin_addr)<<endl;
}
else if(events[i].events&EPOLLIN) //客户端发来数据
{
server_recv(events[i].data.fd); //接收数据包并做处理
}
if(events[i].events&EPOLLRDHUP) //客户端退出
{
cout<<"a connet is quit,ip is "<<inet_ntoa(cli_addr.sin_addr)<<endl;
epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,&event);
close(events[i].data.fd);
}
}
}
}
客户端代码:
#include<iostream>
#include<string.h>
#include<math.h>
#include<sys/signal.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<time.h>
#define PORT 6666 //服务器端口
#define NET_PACKET_DATA_SIZE 5000
using namespace std;
struct File_message //文件信息类
{
char filename[100]; //文件名
long filesize; //文件大小
};
struct File_data //文件内容类
{
char filename[100]; //文件名
char buffer[1024]; //文件内容
};
void my_err(const char *err_string,int line) //自定义错误函数
{
cerr<<"line:"<<line<<endl; //输出错误发生在第几行
perror(err_string); //输出错误信息提示
exit(1);
}
/// 网络数据包包头
struct NetPacketHeader
{
unsigned short wDataSize; ///< 数据包大小,包含封包头和封包数据大小
unsigned short wOpcode; ///< 操作码
};
/// 网络数据包
struct NetPacket
{
NetPacketHeader Header; ///< 包头
unsigned char Data[NET_PACKET_DATA_SIZE]; ///< 数据
};
/// 网络操作码
enum eNetOpcode
{
NET_TEST1 = 1, //发送文件信息
NET_TEST2 = 2, //发送文件内容
};
bool send_to_serv(int conn_fd,char *data_buffer,int datasize,uint16_t wOpcode) //向服务器发送数据
{
NetPacket send_packet;
send_packet.Header.wDataSize=datasize+sizeof(NetPacketHeader); //数据包大小
send_packet.Header.wOpcode=wOpcode;
memcpy(send_packet.Data,data_buffer,datasize);
if(send(conn_fd,&send_packet,send_packet.Header.wDataSize,0))
return true;
else
return false;
}
bool send_file(int conn_fd) //向服务器发送文件
{
char send_buffer[1024]; //发送数据buffer
string filename; //文件路径名
int fd; //文件描述符
struct stat file_buffer; //文件属性buffer
File_message file_message; //文件信息
File_data file_data;
int nsize=0;
int sum_size=0;
int ss=0;///////////////////////////
memset(send_buffer,0,sizeof(send_buffer));
cout<<"请输入要发送的文件路径及文件名"<<endl;
getline(cin,filename);
if(fd=open(filename.c_str(),O_RDONLY)<0)
{
my_err("open file error",__LINE__);
}
if(stat(filename.c_str(),&file_buffer)<0)
{
my_err("stat file error",__LINE__);
}
strcpy(file_message.filename,filename.c_str());
file_message.filesize=file_buffer.st_size;
if(send_to_serv(conn_fd,(char*)&file_message,sizeof(file_data),NET_TEST1)<0)
{
cout<<"向服务器发送文件信息失败"<<endl;
}
cout<<++ss<<":"<<"datasize "<<sizeof(file_data)<<endl; //////////////////
close(fd);
if((fd=open(filename.c_str(),O_RDONLY))<0)
{
my_err("打开文件失败",__LINE__);
}
while(nsize=read(fd,send_buffer,sizeof(send_buffer)))
{
memset(&file_data,0,sizeof(file_data));
strcpy(file_data.filename,filename.c_str());
strncpy(file_data.buffer,send_buffer,nsize);
send_to_serv(conn_fd,(char*)&file_data,nsize+sizeof(file_data.filename),NET_TEST2);
cout<<++ss<<":"<<"datasize "<<sizeof(file_data.filename)+nsize<<endl;///////////
sum_size+=nsize;
}
if(sum_size==file_buffer.st_size)
{
cout<<"发送文件成功"<<endl;
close(fd);
return true;
}
else
{
cout<<"发送文件出错"<<endl;
close(fd);
return false;
}
}
int main(int argc ,char **argv)
{
int conn_fd; //创建连接套接字
struct sockaddr_in serv_addr; //储存服务器地址
if(argc!=3) //检测输入参数个数是否正确
{
cout<<"Usage: [-a] [serv_address]"<<endl;
exit(1);
}
//初始化服务器地址结构
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(PORT);
//从命令行服务器地址
for(int i=0;i<argc;i++)
{
if(strcmp("-a",argv[i])==0)
{
if(inet_aton(argv[i+1],&serv_addr.sin_addr)==0)
{
cout<<"invaild server ip address"<<endl;
exit(1);
}
break;
}
}
//检查是否少输入了某项参数
if(serv_addr.sin_addr.s_addr==0)
{
cout<<"Usage: [-a] [serv_address]"<<endl;
exit(1);
}
//创建一个TCP套接字
conn_fd=socket(AF_INET,SOCK_STREAM,0);
if(conn_fd<0)
{
my_err("connect",__LINE__);
}
//向服务器发送连接请求
if(connect(conn_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))<0)
{
my_err("connect",__LINE__);
}
send_file(conn_fd); //向服务器发送文件
sleep(60);
}
改用unsigned char 数组作为读写数据的类型;不要用char数组
对比双方的数据是否一致。。。还有文件的文件格式。。。