http://blog.csdn.net/hdw10/article/details/7497760
最近看到很多网友写的socket程序,试验了几个,发送"Hello,world"时都没有问题,但在传输较大文件,比如一个200M的文件时,都不能成功,即使成功了,server端也存在保存的问题, 有的是乱码,也有文件大小不对的情况,这两天工作不忙,我也草草写了一些代码,实现了可以传输任意格式任意大小的文件,几位同事帮我一起做了测试,传送几百M的文件速度还是很快的,而且也没有丢数据的问题。 代码原理很简单,所以也没有做注释,大家可以把代码copy下来,自己把玩一下。现在我跟同事之间传送datasheet,都在使用我的程序, 其实我也是个入行不深的人,代码并没有经过优化,很多printf() 是多余的,也有的地方写的很麻烦。希望大家给以批评,给以帮助。
由于 网络接口MTU的限制(一般mtu为1500),大些的文件只能分多次发送,这样就有几个问题:分几次发送?一次发送多大? 保存端的怎么保存?
我的办法是:通过定义一个shouldoplen,来说明一次操作需要操作的长度,如果要发送的文件较小(跟buf相比),shouldoplen 就是读取的文件大小,如果文件较大,需要多次发送,那么shouldoplen 就是buf 的长度,通过多次读取,发送, 直到发送出去的总长度oplencount 等于文件的大小,这时一个文件就算完整发送成功了。
编译时:
gcc socket_server.c -lpthread -o server
gcc socket_client.c -o client
使用时:
在一端打开server
#./server
令一端使用client
#./client ./han/docunt/Linux_dd.pdf Linux_dd.pdf
这样就把./han/docunt/Linux_dd.pdf 这就文件 发送server端,保存名为Linux_dd.pdf
当然,前提是在sock_client.c 中的把目的IP改为你要连接的目的IP。
***********************这是data.h****************
#ifndef DATA_H
#define DATA_H
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<signal.h>
#include<pthread.h>
#include<sys/socket.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include "data.h"
typedef unsigned int uint;
struct data{
char filename[30]; //要发送的文件名(可含路径)
char filesavename[30]; //要保存的文件名(可含路径)
uint filelen; //文件从字节数
uint shouldoplen; //一次要操作的字节数
uint oplencount; // 操作的字节总数
char filebuf[1300]; //由于mtu为1500,在不改动mtu的情况下,1300没有问题。
};
struct data_return {
int writelen; //server端保存文件时,一次写的长度
int writelencount; //写的总数
int recvlen; //一次接受到的长度
};
#endif
***********************这是sock_sever 端********************************
#include "data.h"
int sockid;
pthread_t tid;
int fileopen;
__sighandler_t f_quit(void)
{
printf("\nserver closed right now!!\n");
close(sockid);
close(fileopen);
exit(0);
}
struct thread_para{
int sockid;
struct sockaddr_in addr;
struct sockaddr_in fromaddr;
struct data datarecv;
};
int main(int argc,char* argv[])
{
signal(SIGINT,(__sighandler_t)f_quit);
struct thread_para thread_do;
memset(&thread_do,0,sizeof(thread_do));
sockid = socket(AF_INET,SOCK_STREAM,0);
if(sockid == -1)
printf("socker create error!"),exit(-1);
printf("sockid = %d\n",sockid);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = INADDR_ANY;
int r = bind(sockid,(struct sockaddr*)&addr,sizeof(addr));
if(r<0)
printf("bind error!,r=%s\n",strerror(errno)),exit(-1);
printf("bind success!\n");
if((r = listen(sockid,10))<0)
printf("listen error!,r=%s\n",strerror(errno)),exit(-1);
printf("listen successfully!\n");
int fd,writelen,writelencount,recvlen,opnumber;
struct sockaddr_in fromaddr;
socklen_t len = sizeof(fromaddr);
void* handler(void*);
while(1){
if( (fd = accept(sockid,(struct sockaddr*)&fromaddr,&len)) < 0 ){
printf("accept error,errno = %s\n",strerror(errno)),exit(-1);
}
printf("accept successfuluy!,new sockid=%d\n",fd);
memset(&thread_do,0,sizeof(thread_do));
thread_do.sockid = fd;
thread_do.fromaddr = fromaddr;
pthread_create(&tid,NULL,handler,&thread_do);
}
}
void* handler(void* p)
{
struct thread_para * thread_do = p;
int writelen,writelencount,recvlen,opnumber;
writelen = 0;
writelencount = 0;
recvlen = 0;
opnumber = 1;
printf(" there are the data from %s :\n",inet_ntoa(thread_do->fromaddr.sin_addr) );
do{
memset(&thread_do->datarecv,0,sizeof(thread_do->datarecv));
recvlen = recv( thread_do->sockid,&thread_do->datarecv,sizeof(thread_do->datarecv)-1,0);
if(recvlen<0)
printf("recv error!,errro=%s\n",strerror(errno)),exit(-1);
else if(recvlen == 0 )
printf(" client has closed!\n");
else
printf("the filename from client:\n\t%s\n",thread_do->datarecv.filename);
printf("the file save name:\n\t%s\n",thread_do->datarecv.filesavename);
printf("the filelen =%d,the recvlen=%d,the filebuf = %d\n",thread_do->datarecv.filelen,recvlen,sizeof(thread_do->datarecv.filebuf)-1 );
printf("the shouldoplen = %d\n",thread_do->datarecv.shouldoplen);
printf("the oplencount = %d\n",thread_do->datarecv.oplencount);
umask(0);
int fileopen = open((const char*)thread_do->datarecv.filesavename,O_RDWR | O_CREAT | O_APPEND, S_IRWXU | S_IRWXG | S_IROTH );
if(fileopen<0){
printf("fileopen error!,%s\n",strerror(errno)),exit(-1);
}
printf("file %s create and open successfully!\n",thread_do->datarecv.filesavename);
if( ( writelen = write(fileopen,thread_do->datarecv.filebuf,thread_do->datarecv.shouldoplen) ) <0 ){
printf("write error!\n"),exit(-1);
}
writelencount += writelen;
printf(" opnumber = %d,writelen=%d,writelencount=%d\n \n",opnumber++,writelen,writelencount);
struct data_return data_r;
data_r.writelen = writelen;
data_r.writelencount = writelencount;
data_r.recvlen = recvlen;
write(thread_do->sockid, &data_r ,sizeof(data_r));
close(fileopen);
}while( writelencount != thread_do->datarecv.filelen );
}
***********************************这是sock_client 端*******************************
#include "data.h"
int sockid;
__sighandler_t f_quit(void)
{
printf("\n client closed right now!!\n");
close(sockid);
exit(0);
}
int main(int argc,char* argv[])
{
signal(SIGINT,(__sighandler_t)f_quit);
sockid = socket(AF_INET,SOCK_STREAM,0);
if(sockid==-1){
printf("socket create error!"),exit(-1);
}
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
inet_aton("192.168.10.52",&addr.sin_addr);
int r = connect(sockid,(struct sockaddr*)&addr,sizeof(addr));
if(r<0)printf("connect error!\n"),exit(-1);
printf("connect success! \n");
struct data datasent;
memset(&datasent,0,sizeof(datasent));
struct data_return data_r;
memset(&data_r,0,sizeof(data_r));
strcpy(datasent.filename, argv[1]);
strcpy(datasent.filesavename,argv[2]);
int fd = open(datasent.filename,O_RDWR);
if(fd<0){
printf("open %s failed!\n",datasent.filename),exit(-1);
}
datasent.filelen = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
printf("open file %s success!,the fd = %d,file length = %d\n",datasent.filename,fd,datasent.filelen);
datasent.oplencount = 0;
int opnumber = 1;
do{
lseek(fd,datasent.oplencount,SEEK_SET);
if( (datasent.filelen - datasent.oplencount ) >= ( sizeof(datasent.filebuf) - 1 ) ){ // 比较: ( filelen - oplencount ) 与 filebuf 的大小
datasent.shouldoplen = sizeof(datasent.filebuf) - 1 ; //通过比较,确定出shouoplen的值
}
else{
datasent.shouldoplen = datasent.filelen -datasent.oplencount;
}
memset(&datasent.filebuf,0,sizeof(datasent.filebuf));
readdata(fd,&datasent,datasent.shouldoplen);
datasent.oplencount += datasent.shouldoplen;
sentdata(datasent);
printf(" opnumber = %d,shouldoplen = %d ,datasent.oplencount = %d \n",opnumber++,datasent.shouldoplen,datasent.oplencount);
read(sockid,&data_r,sizeof(data_r));
printf("server info:recvlen = %d, writelen = %d, writelencount = %d \n\n",data_r.recvlen,data_r.writelen,data_r.writelencount);
} while( datasent.filelen > datasent.oplencount );
close(sockid);
close(fd);
}
int readdata(int fd,struct data* datasent,int len)
{
int readlen;
if((readlen = read( fd,datasent->filebuf,len ) )<0){ //通过len来确定一次要读多少
printf("%m\n"),exit(-1);
}
printf("read %s successfully ! readlen=%d\n",datasent->filename,readlen);
}
int sentdata(struct data datasent)
{
int sendlen;
if((sendlen = send( sockid,&datasent,sizeof(datasent)-1,0 ))<0){
printf("write error ,writelen=%d, sizeof(datasent): %d, error: %s\n",sendlen, sizeof(datasent), strerror(errno));
exit(-1);
}
printf("file %s send successfully!,sendlen=%d\n",datasent.filename,sendlen);
}