您的位置:首页 > 编程语言

vc编写的用UDP协议传输文件的代码

2011-11-25 09:51 441 查看
UDP本身是一种无连接的协议。它只管发送,而不需要知道,发送的包是不是准确的到达了目的地。所以它具有发送效率高的特点,同时也具有丢包的弱点。但如果对udp加一些验证和重发机制,就能很大程度上避免丢包的情况,达到稳定的传输。同时,此种方式的传输速度会比TCP方式的传输快很多。以下是一种串行的带验证重发机制的UDP传输文件,源代码,client负责发文件,server负责接文件。希望对大家有帮助。程序中,发送的包是自己定义的,这样除了可以将需要发送的数据发送过去外,还可以将一些控制信息发过去。

SERVER端:

#include<winsock.h>

#include<stdio.h>

#include<windows.h>

#include<conio.h>

#include<memory.h>

#pragma comment( lib, "ws2_32.lib" )

#define PORT 8000

#define SERVER "192.168.1.211"

SOCKET sock;

int sendexit=0;//控制发送线程状态的全局变量

int recvexit=0;//控制接收程状态的全局变量

int filesize=1;//记录文件大小的全局变量

int recvsize=0;//记录文件大小的全局变量

int id=1;

sockaddr_in server;

int len =sizeof(server);

struct baohead//包头

{

int size;

int id;

int recvsize;

};

typedef baohead ElemType;

baohead datahead;

struct recvbuf//包格式

{

ElemType head;//包头

char buf[1024];//存放数据的变量

int bufSize;//存放数据长度的变量

};

struct recvbuf data;

DWORD WINAPI recvfunc(LPVOID lpParam);//接收线程

int main()

{

WSADATA wsadata;

WSAStartup(MAKEWORD(2,2),&wsadata);

sock=socket(AF_INET,SOCK_DGRAM,0);//建立SOCKET

if(sock==SOCKET_ERROR)

{

printf("socket创建失败\n");

return 0;

}

sockaddr_in addr;

addr. sin_family=AF_INET;

addr. sin_port= htons(PORT);

addr. sin_addr.s_addr= inet_addr(SERVER);

int nResult=bind(sock,(sockaddr*)&addr,sizeof(addr));//绑定SOCKET

if(nResult==SOCKET_ERROR)

{

printf("绑定SOCKET有问题. \n");

return 0;

}

else

{

printf("服务启动成功!\n");

}

DWORD ID;

HANDLE handle=CreateThread(NULL,0,recvfunc,0,0,&ID);//创建接收线程

int i=0;

if((recvexit!=1) && (i++<40))//主线程为创建接收线程等待40秒,如果创建成功则不等待。

{

Sleep(1000);

}

if(recvexit!=1)//40秒后还未创建成功则显示创建失败。

{

printf("接收线程创建失败!\n");

return 0;

}

else

{

printf("接收线程创建成功!\n");

}

while(recvexit<2)//如果接收线程未退出,则主线程睡一段时间。

{

Sleep(100);

}

printf("退出成功!\n");

return 0;

}

DWORD WINAPI recvfunc(LPVOID lpParam)

{

recvexit=1;//告诉主线程,接收线程已经创建成功。

FILE *fp;

fp=fopen("E:\\1.exe", "wb");//如果原文件存在,则将原来文件删除。并新建一个空文件

fclose(fp);

fp=fopen("E:\\1.exe", "ab");//打开那个空文件。

FD_SET fdread;//建立集合

while( recvexit==1 )

{

FD_ZERO(&fdread);//清空结合

FD_SET(sock,&fdread);//将socket放进集合内

timeval val;//设置超时时间

val.tv_sec=60;

val.tv_usec=0;

int Result=select(0,&fdread,0,0,&val);//检查sokcet的可读性

if(Result==SOCKET_ERROR)//错误则退出线程

{

break;

}

if(Result==0)//没数据到则马上返回。

{

continue;

}

int rec = recvfrom(sock, (char *)(&data), 2048, 0,(sockaddr *)&server,&len);//读取到来的数据报

if( rec > 0 )

{

if(data.head.id == id)//包ID是所期望的,则读入数据

{

// printf("id=%d\n",data.head.id);

// printf("bufsize=%d\n",data.bufSize);

fwrite( data.buf, sizeof(char), data.bufSize, fp);

recvsize=recvsize+data.bufSize;//记录新的接收到的文件长度

// printf("recvsize=%d\n",recvsize);

filesize=data.head.size;

datahead.id= data.head.id;

datahead.recvsize=recvsize;

datahead.size=filesize;

int d=sendto(sock,(char*)(&datahead),sizeof(datahead),0,(sockaddr *)&server,len);//发送收到数据的确认包

if(d>0)

{

// printf("发送收到数据包的确认信息成功!\n");

}

id=id+1;

}

else if(data.head.id < id)//如果是已经接收过的包的重发包,则丢弃,并重发一次确认包告诉发送端该包已收。

{

datahead.id = data.head.id;

datahead.recvsize=recvsize;

datahead.size=filesize;

int d=sendto(sock,(char*)(&datahead),sizeof(datahead),0,(sockaddr *)&server,len);

if(d>0)

{

// printf("发送收到数据包的确认信息成功!\n");

}

}

if(filesize == recvsize )//如果接收到的文件长度等于文件大小,

{ //等发送一个确认包告诉发送端文件已接收完成,并退出自己的接收线程

datahead.id = data.head.id;

datahead.recvsize=-1;

datahead.size=filesize;

printf("filesize=%d\n",filesize);

printf("recvsize=%d\n",recvsize);

printf("文件接收完成!\n");

int d=sendto(sock,(char*)(&datahead),sizeof(datahead),0,(sockaddr *)&server,len);

sendexit=2;

recvexit=2;

break;

}

}

}

fclose(fp);

recvexit=2;

return 0;

}

CLIENT端:

#include<winsock.h>

#include<stdio.h>

#include<windows.h>

#include<conio.h>

#include<memory.h>

#pragma comment( lib, "ws2_32.lib" )

#define SERVER "192.168.1.211"

struct baohead//包头格式

{

int size;

int id;

int recvsize;

};

typedef baohead ElemType;

struct sendbuf//数据包格式

{

ElemType head;//包头

char buf[1024];//存放数据的变量

int bufSize;//存放本包存放数据的buf变量的长度的变量。

};

struct sendbuf data;

DWORD WINAPI sendfunc(LPVOID lpParam);//发送线程

DWORD WINAPI recvfunc(LPVOID lpParam);//街收线程

SOCKET sock;

int sendexit=0;//控制发送线程状态的全局变量

int recvexit=0;//控制接收线程状态的全局变量

int filesize=0;//记录文件大小的全局变量

int recvsize=0;//返回已接收文件大小的全局变量

sockaddr_in server;

int len =sizeof(server);

int Sendid=0;//发送ID

int ReceiveId =0;//接收ID

HANDLE hEvent;//句炳

int main()

{

hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//创建一个事件

WSADATA wsadata;

WSAStartup(MAKEWORD(2,2),&wsadata);

sock=socket(AF_INET,SOCK_DGRAM,0);

if(sock==SOCKET_ERROR)

{

printf("socket创建失败\n");

return 0;

}

else

{

printf("服务启动成功!\n");

}

server.sin_family=AF_INET;

server.sin_port=htons(8000); ///server的监听端口

server.sin_addr.s_addr=inet_addr(SERVER); ///server的地址

getch();

DWORD ID;

HANDLE handle=CreateThread(NULL,0,recvfunc,0,0,&ID);//创建接收线程

int i=0;

if((recvexit!=1) && (i++<40))//主线程等待40秒为创建接收线程,

{

Sleep(1000);

}

if(recvexit!=1)//如果40秒后任未创建成功,显示创建失败。

{

printf("接收线程创建失败!\n");

return 0;

}

else

{

printf("接收线程创建成功!\n");

}

handle=CreateThread(NULL,0,sendfunc,0,0,&ID);//创建发送线程

int j=0;

if(sendexit!=1 && j++<40)//主线程为创建发送线程等待40秒

{

Sleep(1000);

}

if(sendexit!=1)//40秒后还未创建,则显示创建失败。

{

printf("发送线程创建失败!\n");

return 0;

}

else

{

printf("发送线程创建成功!\n");

}

while(recvexit<2 ||sendexit<2 )//如果发送和接收线程未全部退出,则主线程等待。

{

Sleep(100);

}

printf("退出成功!\n");

return 0;

}

DWORD WINAPI sendfunc(LPVOID lpParam)

{

sendexit=1;//告诉主线程,发送线程已经创建完毕

static int filepos=0;//记录文件偏移量的变量

FILE *fp1;

fp1 = fopen( "D:\\1.exe", "rb" );//打开要发送的文件

fseek(fp1,0,SEEK_END);//将文件指针移动到文件尾部

filesize=ftell(fp1);//得到文件长度。

// printf("filesize=%d\n",filesize);

rewind(fp1);//将文件指针重新指到头部

while( sendexit==1 )

{

if(ReceiveId == Sendid) //确认包头内的ID是所期望的。

{

Sendid++;

fseek(fp1,filepos,SEEK_SET);//根据偏移量移动文件指针

int readsize=fread(data.buf,sizeof(char),1024,fp1);//读取下段文件

filepos = ftell(fp1);

data.head.size=filesize;//将文件大小放进包头

data.head.id=Sendid;//发送ID放进包头(标志包顺序)

data.bufSize = readsize;//记录数据段长度的变量

// printf("id=%d\n",Sendid);

// printf("sendsize=%d\n",data.bufSize);

// printf("filesize=%d\n",data.head.size);

int d=sendto(sock,(char*)(&data),sizeof(data),0,(sockaddr *)&server,len);//发送数据报

if(d>0)//成功发送

{

// printf("发送数据%s\n",(char*)(&data));

}

}

DWORD dRet=WaitForSingleObject(hEvent,500);//发送完后,等待500毫秒。等待中如果等待的事件被激活,则马上返回。

switch(dRet)

{

case WAIT_TIMEOUT://一段时间内没收到确认包,重发刚才的那个数据报

sendto(sock,(char*)(&data),sizeof(data),0,(sockaddr *)&server,len);

// printf("重新发送数据%s\n",(char*)(&data));

break;

default:break;//否则返回。

}

}

fclose(fp1);

sendexit=2;

return 0;

}

DWORD WINAPI recvfunc(LPVOID lpParam)

{

recvexit=1;//告诉主线程,接收线程已经启动。

baohead recvbao;

FD_SET fdread;

while( recvexit==1 )

{

FD_ZERO(&fdread);

FD_SET(sock,&fdread);

timeval val;

val.tv_sec=60;

val.tv_usec=0;

int Result=select(0,&fdread,0,0,&val);//查看socket是否可读

if(Result==SOCKET_ERROR)

{

break;

}

if(Result==0)//不可读则马上返回。重新判断

{

continue;

}

int rec = recvfrom(sock, (char *)(&recvbao), 2048, 0,(sockaddr *)&server,&len);//可读状态下接收数据报

if( rec > 0 )

{

// printf("收到确认信息 %s\n" , (char*)(&recvbao));

recvsize=recvbao.recvsize;//将接收大小传给全局变量

ReceiveId = recvbao.id;//记录此次接收到包的ID

SetEvent(hEvent);//激活事件

if(recvbao.recvsize==-1)//如果接收端发送接收完毕的信号,则发送,接收线程均自行退出,并显示发送文件完成

{

sendexit=2;

recvexit=2;

printf("文件发送完成!\n");

break;

}

}

}

recvexit=2;

return 0;

}

UDP本身是一种无连接的协议。它只管发送,而不需要知道,发送的包是不是准确的到达了目的地。所以它具有发送效率高的特点,同时也具有丢包的弱点。但如果对udp加一些验证和重发机制,就能很大程度上避免丢包的情况,达到稳定的传输。同时,此种方式的传输速度会比TCP方式的传输快很多。以下是一种串行的带验证重发机制的UDP传输文件,源代码,client负责发文件,server负责接文件。希望对大家有帮助。程序中,发送的包是自己定义的,这样除了可以将需要发送的数据发送过去外,还可以将一些控制信息发过去。

本程序,虽然是串行传输的,但由于是UDP所以传输速度比起TCP传输会快上很多。如果你用循环队列,把UDP的传输方式设计成并行多包发送的话,速度还会快很多。其中的一些输出语句是用来查看程序的传输状态和进度的。因为影响传输速度,被我屏蔽了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: