tcp与udp的区别:
1.UDP处理的细节比TCP少
2.UDP不能保证消息被传送到目的地
3.UDP不能保证数据包的传递顺序
4.TCP处理UDP不处理的细节
5.UDP是无连接的
6.TCP是保持一个连接
7.UDP只是把数据发送出去而已(像写信一样)
udp应用场景:
1.视频
2.广播(tcp是没有广播功能的)
udp特有函数:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
注意:addrlen这个参数,一定初始化,int len=sizeof(src_addr);
manpage的资料:
The argument
addrlen is a value-result argument, which the
caller should initialize before the call to the
size of the buffer associated with src_addr, and
modified on return to indicate the actual size of
the source address. The returned address is trun-
cated if the buffer provided is too small; in this
case, addrlen will return a value greater than was
supplied to the call.
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
实例
将上一篇文章filetransfer-tcp改为filetransfer-udp,比较差异
/*
============================================================================
Name : myfiletransfer-udpt.c
Author : Allen
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include"pub.h"
int main(int arg ,char*args[])
{
if(arg<2)
{
printf("usage:server port\n");
return 0;
}
int port=atoi(args[1]);
printf("recving begin\n");
if((recv_work(port))==1)
{
printf("recv success\n");
}else
{
printf("sock disconnect\n");
}
return 0;
}
/*
* client.c
*
* Created on: 2016年10月24日
* Author: Administrator
*/
#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include"pub.h"
int main(int arg ,char*args[])
{
if(arg<4)
{
printf("usage:client hostname port filename\n");
return 0;
}
int port=atoi(args[2]);
printf("send begin\n");
printf("%s send begin\n", args[3]);
if((send_work(args[1],port,args[3]))==1)
{
printf("send success\n");
}else
{
printf("sock disconnect\n");
}
return 0;
}
/*
* pub.h
*
* Created on: 2016年10月24日
* Author: Administrator
*/
#ifndef PUB_H_
#define PUB_H_
int send_work(const char*hostname,int port,const char*filename);
int recv_work(int port);
#endif /* PUB_H_ */
/*
* pub.c
*
* Created on: 2016年10月24日
* Author: Administrator
*/
#ifdef WIN
#include <WinSock2.h>
#else
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define SOCKET int
#endif
#include <stdio.h>
#include "pub.h"
#define MAXBUFSIZE 16384 //16k
void getfilename(const char *filename, char *name) //从完整路径名中解析出文件名称,例如:/home/test/abc.txt,解析完成后为abc.t
{
int len = strlen(filename);
int i;
for (i = (len - 1); i >= 0; i--)
{
if ((filename[i] == '\\') || (filename[i] == '/')) //为了兼容linux与windows的路径,同时解析/与\分隔的路径名称
{
break;
}
}
strcpy(name, &filename[i + 1]);
return;
}
SOCKET init_socket() //初始化socket
{
//如果是windows,执行如下代码
#ifdef WIN
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(1, 1);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
#endif
return 0;
}
SOCKET create_recv_socket(int port)
{
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0); //建立UDPsocket SOCK_DGRAW
if (st == -1)
{
printf("socket failed %s\n", strerror(errno));
return 0;
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(st, (struct sockaddr*) &addr, sizeof(addr)) == -1)
{
printf("bind failed %s\n", strerror(errno));
return 0;
}
return st;
}
SOCKET create_send_socket()
{
if (init_socket() == -1)
{
return 0;
}
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);
if (st == -1)
{
printf("socket failed %s\n", strerror(errno));
return 0;
}
return st;
}
int send_work(const char*hostname, int port, const char*filename)
{
SOCKET send_st = create_send_socket();
if (send_st == 0)
{
return 0;
}
SOCKET recv_st = create_recv_socket(port); //??为什么port要加1
if (recv_st == 0)
{
return 0;
}
FILE*fp = fopen(filename, "rb");
if (fp == NULL)
{
printf("fopen failed %s\n", strerror(errno));
return 0;
}
char *buf = malloc(MAXBUFSIZE);
memset(buf, 0, MAXBUFSIZE);
getfilename(filename, buf);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(hostname);
size_t rc = sendto(send_st, buf, strlen(buf), 0,
(struct sockaddr *) &server_addr, sizeof(server_addr));
if (rc <= 0)
{
printf("sendto failed %s\n", strerror(errno));
} else //发送成功
{
struct sockaddr_in client_addr;
//如果为windows执行如下代码
#ifdef WIN
int len = 0;
//如果是linux执行如下代码
#else
unsigned int len = 0;
#endif
len = sizeof(client_addr); //??????
/* The argument
addrlen is a value-result argument, which the
caller should initialize before the call to the
size of the buffer associated with src_addr, and
modified on return to indicate the actual size of
the source address.*/
memset(&client_addr, 0, sizeof(client_addr));
memset(buf, 0, MAXBUFSIZE);
if (recvfrom(recv_st, buf, MAXBUFSIZE, 0,
(struct sockaddr*) &client_addr, &len) <= 0)
{
printf("recvfrom failed %s\n", strerror(errno));
} else
{
if (strncmp(buf, "OK", 2) == 0) //接受来自server端同意发送信号
{
while (1)
{
memset(buf, 0, MAXBUFSIZE);
rc = fread(buf, 1, MAXBUFSIZE, fp);
if (rc <= 0)
{
break;
} else
{
rc = sendto(send_st, buf, rc, 0,
(struct sockaddr *) &server_addr,
sizeof(server_addr));
if (rc <= 0)
{
printf("sendto failed %s\n", strerror(errno));
break;
}
}
}
}
memset(buf, 0, MAXBUFSIZE);
rc = sendto(send_st, buf, 128, 0, (struct sockaddr *) &server_addr,
sizeof(server_addr));
}
}
fclose(fp);
free(buf);
#ifdef WIN
closesocket(send_st);
closesocket(send_st);
WSACleanup();
#else
close(send_st);
close(recv_st);
#endif
return 1;
}
int recv_work(int port)
{
SOCKET send_st = create_send_socket();
if (send_st == 0)
{
return 0;
}
SOCKET recv_st = create_recv_socket(port);
if (recv_st == 0)
{
return 0;
}
FILE *fp = NULL;
char *buf = malloc(MAXBUFSIZE);
#ifdef WIN
int len = 0;
#else
unsigned int len = 1;
#endif
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
memset(buf, 0, MAXBUFSIZE);
len = sizeof(client_addr); //?????
size_t rc = recvfrom(recv_st, buf, MAXBUFSIZE, 0,
(struct sockaddr*) &client_addr, &len);
if (rc <= 0)
printf("recvfrom failed %s\n", strerror(errno));
else
{
printf("recving %s\n", buf);
fp = fopen(buf, "wb");
if (fp == NULL)
{
printf("fopen failed %s\n", strerror(errno));
return 0;
}
memset(buf, 0, MAXBUFSIZE);
strcpy(buf, "OK");
client_addr.sin_port = htons(port); //客户端接收数据的端口号为什么要+1???
rc = sendto(send_st, buf, strlen(buf), 0,
(struct sockaddr*) &client_addr, sizeof(client_addr));
if (rc <= 0)
{
printf("send to failed %s\n", strerror(errno));
}
while (1)
{
memset(buf, 0, MAXBUFSIZE);
rc = recvfrom(recv_st, buf, MAXBUFSIZE, 0,
(struct sockaddr*) &client_addr, &len);
char temp[128];
memset(temp, 0, sizeof(temp));
if (memcmp(buf, temp, sizeof(temp)) == 0)
{
break;
}
if (rc <= 0)
{
printf("recvfrom failed %s\n", strerror(errno));
break;
} else
{
fwrite(buf, 1, rc, fp);
}
}
}
if (fp)
fclose(fp);
free(buf);
#ifdef WIN
closesocket(send_st);
closesocket(recv_st);
WSACleanup();
#else
close(send_st);
close(recv_st);
#endif
return 1;
}
总结:
udp使用方法比tcp简单许多,代码与上一篇tcp应用相似,注释就请参考上一篇博文。
在测试过程中,传送一个74M的视频文件,server端没有收到完整的视频文件,这一点证明了udp不能保证数据被传送到目的地,容易丢失。