UDP通信简单说来就是向固定IP发送数据,而不需要必须确认对方是否存在。通信过程简单,无需握手。当然也无法判断是否接收正常。
UDP编程过程:
基本函数
建立套接字
socket函数:为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。sockfd=socket(AF_INET,SOCK_DGRAM,0)
第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。。
bind函数:为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。
int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen);
第一个参数是socket函数返回的套接口描述字;第二和第第三个参数分别是一个指向特定于协议的地址结构的指针和该地址结构的长度。
- recvfrom函数:UDP使用recvfrom()函数接收数据,他类似于标准的read(),但是在recvfrom()函数中要指明目的地址
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * from, size_t *addrlen);
返回接收到数据的长度---成功 -1---失败 0—无数据
第一个参数套接字,第二个接受数组,第三个参数缓冲区长度,flags参数是传输控制标志,一般为0。from表示sockaddr结构地址,最后一个为sockaddr 结构大小
- sendto函数:UDP使用sendto()函数发送数据,他类似于标准的write(),但是在sendto()函数中要指明目的地址。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen);
返回发送数据的长度---成功 -1---失败
前三个参数等同于函数read()的前三个参数,flags参数是传输控制标志。参数to指明数据将发往的协议地址,他的大小由addrlen参数来指定。
- select函数:
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
- int maxfdp是一个整数值,指集合中所有文件描述符的范围,即所有文件描述符的最大值加1
- fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括读取文件描述符
- fd_set *writefds是指向fd_set结构的指针,这个集合中应该写包括文件描述符
- fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常
- struct timeval* timeout是select的超时时间,这个参数至关重要.超时时间内为阻塞,超时则调出,返回0值。错误返回-1;
服务端
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define PORT 1234
#define MAXDATASIZE 100
main()
{
int sockfd;
struct sockaddr_in server;
socklen_t addrlen;
int num;
char buf[MAXDATASIZE];
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("Creatingsocket failed.");
exit(1);
}
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr.s_addr= htonl (INADDR_ANY);
if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
perror("Bind()error.");
exit(1);
}
addrlen=sizeof(server);
while(1)
{
num =recvfrom(sockfd,buf,MAXDATASIZE,0,(struct sockaddr*)&server,&addrlen);
if (num < 0)
{
perror("recvfrom() error\n");
exit(1);
}
buf[num] = '\0';
printf("You got a message (%s%) from client.\nIt's ip is%s, port is %d.\n",buf,inet_ntoa(client.sin_addr),htons(client.sin_port));
sendto(sockfd,"Welcometo my server.\n",22,0,(struct sockaddr *)&client,addrlen);
close(sockfd);
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define UDP_TEST_PORT 8001
#define UDP_SERVER_IP "192.168.60.169"
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
int sockfd, len = 0;
socklen_t addr_len = sizeof(struct sockaddr_in);
char buffer[256];
/* 建立socket,注意必须是SOCK_DGRAM */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
exit(1);
}
/* 填写sockaddr_in*/
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(UDP_TEST_PORT);
addr.sin_addr.s_addr = inet_addr(UDP_SERVER_IP);
close(sockfd);
buffer="hello";
while(1) {
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&addr, addr_len);
sleep(2); /*睡眠2秒*/
printf("%s\n",buffer);
}
}
Select
由于recvfrom会造成程序堵塞,故而UDP一般可配合select函数使用
fd_set fds;
timeval timeout={time,0};
int net;
while(1)
{
timeout.tv_sec=time;
timeout.tv_usec=0;
FD_ZERO(&fds); //描述符情空
FD_SET(sockfd,&fds);//将套接字绑定描述符
net=0;
net=select(sockfd+1,&fds,NULL,NULL,&timeout);
if(net<0)
{
exit(-1);
}
else if(net==0)
{
printf("timeout\n");
}
else
{ if(FD_ISSET(sockfd,&fds))
{
num =recvfrom(sockfd,buf,MAXDATASIZE,0,(struct sockaddr*);
}
}
}