背景:
UDP套接口是无连接的、不可靠的数据报协议;既然他不可靠为什么还要用呢?其一:当应用程序使用广播或多播时只能使用UDP协议;其二:由于他是无连接的,所以速度快。因为UDP套接口是无连接的,如果一方的数据报丢失,那另一方将无限等待,解决办法是设置一个超时。
建立UDP套接口时socket函数的第二个参数应该是SOCK_DGRAM,说明是建立一个UDP套接口;由于UDP是无连接的,所以服务器端并不需要listen或accept函数。
使用UDP套接字编程可以实现基于TCP/IP协议的面向无连接的通信,它分为服务器端和客户端两部分,其主要实现过程如图所示:
代码的实现过程:
UDP服务器
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc,char* argv[])
{
if(argc<3)
{
printf("./server [address] [port]\n");
exit(EXIT_SUCCESS);
}
int sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
perror("socket");
return 1;
}
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(atoi(argv[2]));//主机字节序转换成网络字节序
local.sin_addr.s_addr=inet_addr(argv[1]);//点分十进制的IP地址
//转换成二进制的网络字节序列
//进行绑定端口号
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
return 2;
}
char buf[1024];
struct sockaddr_in client;
while(1)
{
socklen_t len=sizeof(client);
ssize_t s = recvfrom(sock,buf,sizeof(buf)-1,0,\
(struct sockaddr*)&client,&len);
if(s>0)
buf[s]=0;
printf("[%s:%d]:%s\n",inet_ntoa(client.sin_addr),\
ntohs(client.sin_port),buf);
sendto(sock,buf,strlen(buf),0,\
(struct sockaddr*)&client,sizeof(client));
}
}
UDP客户端:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc,char* argv[])
{
if(argc<3)
{
printf("./server [address] [port]\n");
exit(EXIT_SUCCESS);
}
int sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock<0)
{
perror("socket");
return 2;
}
struct sockaddr_in server;
server.sin_family=AF_INET;
server.sin_port=htons(atoi(argv[2]));
server.sin_addr.s_addr=inet_addr(argv[1]);
char buf[1024];
struct sockaddr_in peer;
while(1)
{
socklen_t len=sizeof(peer);
printf("please enter: \n");
fflush(stdout);
ssize_t s =read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1]=0;
sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&server,sizeof(server));
ssize_t s1=recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&peer,&len);
if(s1>0){
buf[s1]=0;
printf("server echo# %s\n",buf);
}
}
}
}
了解涉及到的函数:
1、socket函数
主要执行网络的输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符:
第一个参数指明了协议簇,目前支持5种协议簇,最常用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。
2、bind函数
为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。
bind函数的第一个参数是socket函数返回的套接字接口的文件描述符,后面的两个参数分别表示的是用于某种特定协议的地址结构的指针,和指向该地址结构的长度。
3、recvfrom函数
UDP使用recvfrom进行接收数据,在这里类似于标准的read,不过在这里recvfrom要写入目的地址。
4、sendto函数
UDP使用的进行发数据的函数,类似于write
实现的结果:
最终我们实现在同一个局域网下两个终端之间的对话。
udp客户端:
udp服务器: