`
aspnetwinform
  • 浏览: 84319 次
  • 性别: Icon_minigender_2
  • 来自: 武汉
社区版块
存档分类
最新评论

linux网络编程之socket(十四):基于UDP协议的网络程序

 
阅读更多

一、下图是典型的UDP客户端/服务器通讯过程


下面依照通信流程,我们来实现一个UDP回射客户/服务器


#include <sys/types.h>
#include <sys/socket.h>


ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

当套接字处于“已连接”的状态时,才可以使用send,当flags = 0 时 send 与 write 一致。

且 send(sockfd, buf, len, flags); 即 sendto(sockfd, buf, len, flags, NULL, 0);


ssize_t recv(int sockfd, void *buf, size_t len, int flags);

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

recv 与 recvfrom 的关系与 send 与 sendto 的关系一致。


C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br></nobr>
/*************************************************************************
>FileName:echoser_udp.c
>Author:Simba
>Mail:dameng34@163.com
>CreatedTime:Sun03Mar201306:13:55PMCST
************************************************************************/


#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>

#defineERR_EXIT(m)\
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

voidecho_ser(intsock)
{
charrecvbuf[1024]={0};
structsockaddr_inpeeraddr;
socklen_tpeerlen;
intn;

while(1)
{

peerlen=sizeof(peeraddr);
memset(recvbuf,0,sizeof(recvbuf));
n=recvfrom(sock,recvbuf,sizeof(recvbuf),0,
(structsockaddr*)&peeraddr,&peerlen);
if(n==-1)
{

if(errno==EINTR)
continue;

ERR_EXIT("recvfromerror");
}
elseif(n>0)
{

fputs(recvbuf,stdout);
sendto(sock,recvbuf,n,0,
(structsockaddr*)&peeraddr,peerlen);
}
}
close(sock);
}

intmain(void)
{
intsock;
if((sock=socket(PF_INET,SOCK_DGRAM,0))<0)
ERR_EXIT("socketerror");

structsockaddr_inservaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(5188);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

if(bind(sock,(structsockaddr*)&servaddr,sizeof(servaddr))<0)
ERR_EXIT("binderror");

echo_ser(sock);

return0;
}

C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br></nobr>
/*************************************************************************
>FileName:echocli_udp.c
>Author:Simba
>Mail:dameng34@163.com
>CreatedTime:Sun03Mar201306:13:55PMCST
************************************************************************/


#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

#defineERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

voidecho_cli(intsock)
{
structsockaddr_inservaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(5188);
servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");

intret;
charsendbuf[1024]={0};
charrecvbuf[1024]={0};
while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)
{

sendto(sock,sendbuf,strlen(sendbuf),0,(structsockaddr*)&servaddr,sizeof(servaddr));

ret=recvfrom(sock,recvbuf,sizeof(recvbuf),0,NULL,NULL);
if(ret==-1)
{
if(errno==EINTR)
continue;
ERR_EXIT("recvfrom");
}

fputs(recvbuf,stdout);
memset(sendbuf,0,sizeof(sendbuf));
memset(recvbuf,0,sizeof(recvbuf));
}

close(sock);


}

intmain(void)
{
intsock;
if((sock=socket(PF_INET,SOCK_DGRAM,0))<0)
ERR_EXIT("socket");

echo_cli(sock);

return0;
}

编译运行server,在两个终端里各开一个client与server交互,可以看到server具有并发服务的能力。用Ctrl+C关闭server,然后再运行server,此时client还能和server联系上。和前面TCP程序的运行结果相比较,我们可以体会无连接的含义。


二、UDP编程注意点

1、UDP报文可能会丢失、重复
2、UDP报文可能会乱序
3、UDP缺乏流量控制
4、UDP协议数据报文截断
5、recvfrom返回0,不代表连接关闭,因为udp是无连接的。
6、ICMP异步错误
7、UDP connect
8、UDP外出接口的确定


由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现,即123点所提到的。


对于第4点,可以写个小程序测试一下:

C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br></nobr>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<string.h>

#defineERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)

intmain(void)
{
intsock;
if((sock=socket(PF_INET,SOCK_DGRAM,0))<0)
ERR_EXIT("socket");

structsockaddr_inservaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(5188);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

if(bind(sock,(structsockaddr*)&servaddr,sizeof(servaddr))<0)
ERR_EXIT("bind");

sendto(sock,"ABCD",4,0,(structsockaddr*)&servaddr,sizeof(servaddr));

charrecvbuf[1];
intn;
inti;
for(i=0;i<4;i++)
{
/*udp是报式协议,即若一次性接收的空间小于发来的数据,有可能造成报文截断,
*但一定没有tcp的粘包问题*/

n=recvfrom(sock,recvbuf,sizeof(recvbuf),0,NULL,NULL);
if(n==-1)
{
if(errno==EINTR)
continue;
ERR_EXIT("recvfrom");
}
elseif(n>0)
printf("n=%d%c\n",n,recvbuf[0]);
}
return0;
}

上述程序是自己发送数据给自己,发送了4个字节,但我们只提供1个字节的缓冲区recvbuf,第一次recvfrom 读取一个字节,但接下去循环却读不到剩下的数据了,因为udp 是报式协议,如果一次性接收的缓冲区小于发来的数据,有可能造成报文截断,反观tcp流式协议,可以一次读取一个数据包的一部分,也可以一次性读取多个数据包,但这也正是其会造成粘包问题的来源,所以也说udp 协议不会有粘包问题,因为一次就接收一个消息。输出如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./trunc
n=1 A

............

接收了一个字符之后,再次recvfrom 就阻塞了。


对于第5点,如果我们使用sendto 发送的数据大小为0,则发送给对方的是只含有各层协议头部的数据帧,recvfrom 会返回0,但并不代表对方关闭连接,因为udp 本身没有连接的概念。


第678点合起来一起讲,可以看到我们的客户端程序现在没有调用connect,不运行服务器程序,直接运行客户端程序,查看现象:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echocli_udp
dfsaf

................

当我们在键盘敲入几个字符,sendto只是把Buf的数据拷贝到sock对应的缓冲区中,此时服务器未开启,协议栈返回一个ICMP异步错误,但因为前面没有调用connect“建立”一个连接,则recvfrom时不能收到这个错误而一直阻塞。现在我们在while 循环的外面添加一句:connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)); 再次测试一下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echocli_udp
dfsaf
recvfrom: Connection refused

此时recvfrom 就能接收到这个错误而返回了,并打印错误提示。

其实connect并没有真正建立一个连接,即没有3次握手过程,只是维护了一种状态,绑定了远程地址,因为如此在调用sendto 时也可以不指定远程地址了,如sendto(sock, sendbuf, strlen(sendbuf), 0, NULL, 0); 甚至也可以使用send 函数

send(sock, sendbuf, strlen(sendbuf), 0);

假设现在客户端有多个ip地址,由connect 或 sendto 函数提供的远程地址的参数,系统会选择一个合适的出口,比如远程ip 是192.168.2.10, 而客户端现在的ip 有 192.168.1.32 和 192.168.2.75 那么会自动选择192.168.2.75 这个ip 出去。


参考:

《Linux C 编程一站式学习》

《TCP/IP详解 卷一》

《UNP》
分享到:
评论

相关推荐

    Linux网络编程,包括tcp/upd,select/poll/epoll/pdf

    Linux网络编程是指在Linux操作系统上开发网络应用程序的过程。它主要涉及到TCP/UDP协议以及select/poll/epoll等多路复用技术。 TCP/UDP协议是网络通信的基础,其中TCP协议提供面向连接的可靠数据传输,而UDP协议则...

    tcp+udp.rar_TCP/IP_linux udp socket_linux 网络编程_socket linux udp

    socket网络编程在linux下的实现,支持tcp/udp两种协议

    Linux网络编程socket编程学习

    自学Linux网络编程关于socket的编写,包括 server.c 和 client.c 的编写;很详细的介绍了网络套接字socket的C/S模型TCP协议的服务器端和客户端的程序函数以及编写过程;重点介绍多路I/O转接服务器的实现,包括select...

    Linux网络编程超级详细笔记

    Socket编程:Socket是用于实现网络通信的编程接口,支持基于TCP/IP协议的传输,包括TCP和UDP。在Linux下,使用Socket编程可以实现网络通信,包括客户端和服务器端。 IP地址和端口:IP地址是用于标识网络上的设备的...

    Linux网络编程 视频 教程

    Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口)...

    linux-socket-c.rar_linux socket tcp_linux 网络编程_linux网络编程_socket

    linux网络编程实例,讲解了一些udp tcp的socket编程,有select的例子

    Linux下基于UDP的socket编程,适用多客户端之间,服务器与客户端之间的通信

    Linux下基于UDP的socket编程,适用多客户端之间,服务器与客户端之间的通信。在阿里云服务器上已通过测试。notepad++可打开

    Linux下Socket编程的端口问题 ( Bind(): Address already in use ) PDF版

    Linux Socket Bind() 成功解决socket编程中bind端口绑定的Error:Address already in use 错误。

    Linux网络编程示例,TCP和UDP

    Linux网络编程示例程序,包含TCP、UDP的server和client

    [免费]2018年C++教程网的linux网络编程视频百度云下载链接.rar

    Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口)...

    C++教程网《Linux网络编程》视频百度云地址

    Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口)...

    Linux网络编程 Socket UDP形式(包括服务器端和接收端)

    #include&lt;sys/socket.h&gt; #include #define MYPORT 3490 //设定端口为3490 #define BACKLOG 10 main() { int sockfd,new_fd; struct sockaddr_in my_addr; //服务器网络地址结构体 struct sockaddr_in their_addr;...

    C++教程网视频:linux网络编程

    Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口)...

    linux网络编程socket框架总结

    Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口 Socket接口设计者最先是将接口放在Unix操作...

    Linux网络编程之UDP Socket程序示例

    在网络传输协议中,TCP协议提供的是一种可靠的,复杂的,面向连接的数据流(SOCK_STREAM)传输服务,它通过三段式握手过程建立连接。TCP有一种“重传确认”机制,即接收端收到数据后要发出一个肯定确认的信号,发送...

    基于linux的socket编程.rar

    基于C语言、linux系统的socket编程,包括基本的套接口概念,以及高级编程,有多并发程序,TCP、UDP实例。

    c++教程网的linux网络编程视频下载

    Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口)...

    网络编程课件 Socket编程

    第六章 Socket编程 1、什么是套接字 套接字是一组用于编写网络程序的API 常见的套接字有: --Windows平台 WinSock --Unix/Linux socket 套接字类型: --stream socket 面向流的一种套接字。 --datagram socket...

    Linux网络通信编程socket

    通过学习为后续Linux网络编程奠定基础。首先介绍网络编程的概念,即网络协议分层,旨在帮助读者对网络建立初步的、全面立体的认识,其次介绍包括协议、端口、地址等;最后介绍应用非常广泛的传输控制协议...

Global site tag (gtag.js) - Google Analytics