1. UDP网络编程基础
UDP通信流程
对于UDP方式,发送数据时需要显示指定数据包的目的地址,因此不能使用read/write/send/recv函数。
使用sendto和recvfrom
第一个参数为发送的目标socket对象。
第二个参数为欲发送的数据信息。
第三个参数为发送数据的大小。
第四个参数为flags,如send函数所示。
第五个参数欲发送数据的目标地址,其结构体前面已经介绍。
第六个参数为此结构体的大小。
使用AF_INET实现UDP点对点通信示例
接收端代码
发送端代码
运行结果
接收端
发送端
2. UDP广播通信
单播、组播与广播基本概念
单播:点对点的传送,即一对一的。TCP方式和UDP方式都可以实现单播,且TCP只能是单播的方式。
广播:处于同一个广播域的所有主机都将收到消息,是一点对多点的方式,广播只能由UDP完成。
组播:消息只会从主机发到加入到同一个组播组(例如230.1.1.1)的主机的对应端口,组播也只能由UDP完成。
广播地址是某网段中主机位全为1的IP地址,例如:
单播数据帧格式
广播数据帧格式
允许某个socket发送广播消息
使某个socket可以发送广播消息(修改发送端),需要设置该socket属性为SO_BROADCAST,如下所示:
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
广播信息(目的MAC地址为FF:FF:FF:FF:FF:FF)会被复制并发到同一个广播域内的每个主机的网卡,网卡收到消息后提交给操作系统去处理,操作系统发现有程序在对应端口接收UDP数据则把消息转给相应的程序去处理,如果没有程序接收来自该端口的UDP消息,则操作系统丢弃该消息。
因此,不管主机是否有程序接收广播消息,广播消息一定会被网卡收到并提交给操作系统去处理,所以会造成网络上流量增大,对不接收广播消息的主机造成一定的负担。
UDP广播通信示例
发送端流程:
以UDP方式创建sokcet对象;
设置socket对象为可发送广播消息属性;
将消息以广播方式发送。
接收端流程:
以UDP方式创建socket对象;
绑定接收数据的端口和IP地址,接收端绑定的该主机的IP地址必须设置为INADDR_ANY。否则不能收到消息;
以阻塞方式接收UDP数据;
输出接收到的广播消息。
发送端代码
接收端代码
运行结果
发送端(运行四次)
接收端(可以在同一网段下运行多个接收端)
3. UDP组播通信
组播地址
组播地址范围是D类IP地址,即224.0.0.1-239.255.255.255。
组播MAC地址产生办法
组播数据帧
组播通信编程
在传播时,和广播一样,组播消息会被复制的发到网络上所有主机的网卡,但只有宣布加入该组(例如230.1.1.1)的主机的网卡才会把数据提交给操作系统去处理。如果没有加入组,则网卡直接将数据丢弃。
如果某socket期望接收组播消息,需要设置该socket对象属性,如下所示:
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,sizeof(struct ip_mreq));
组播通信示例
发送端流程如下:以UDP方式创建socket对象;
初始化发送数据所目的地址和端口;
绑定本机IP地址和端口;
向组播组内所有主机发送数据。
接收端流程:
以UDP方式创建socket,获取组播地址和本机地址,将当前主机加入到该组中;
绑定本机IP地址和端口;
接收消息并输出。
发送端代码
接收端代码
运行结果
接收端
发送端
4. socket信号驱动(UDP)
SIGIO信号处理机制
为了使一个套接字使用信号驱动I/O操作,需要至少以下三步操作:(1)安装SIGIO信号,在该处理函数中设定处理办法。
(2)套接字的拥有者必须被设定。一般来说是使用fcntl 函数的F_SETOWN 参数来进行设定拥有者。
(3)套接字必须被允许使用异步I/O。一般是通过调用fcntl 函数的F_SETFL 命令,将即设置为O_ASYNC。
SIGIO 的缺省动作是被忽略。在设置套接字的属主之前必须将SIGIO 的信号处理函数设好,如果以相反的顺序调用这两个函数调用,那么在fcntl 函数调用之后,signal 函数调用之前就有一小段时间程序可能接收到SIGIO 信号。那样的话,信号将会被丢弃。
UDP 套接字的SIGIO 信号
套接字收到了一个数据报的数据包。套接字发生了异步错误。
TCP 套接字的SIGIO 信号
对于一个TCP 套接字来说, SIGIO信号发生的几率太高了, SIGIO 信号不能告诉究竟发生了什么事情。在TCP连接中, SIGIO信号将会在这个时候产生:在一个监听某个端口的套接字上成功的建立了一个新连接。
一个断线的请求被成功的初始化。
一个断线的请求成功的结束。
套接字的某一个通道(发送通道或是接收通道)被关闭。
套接字接收到新数据。
套接字将数据发送出去。
发生了一个异步I/O 的错误。
信号驱动方式处理UDP数据示例
服务器代码
客户端代码
运行结果
服务器
客户端
原文链接
http://blog.csdn.net/geng823/article/details/41865507
sigprocmask()函数实例详解 (2012-07-26 22:48:37)转载▼
功能描述:设定对信号屏蔽集内的信号的处理方式(阻塞或不阻塞)。
用法:
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数:
how:用于指定信号修改的方式,可能选择有三种:
SIG_BLOCK //加入信号到进程屏蔽。
SIG_UNBLOCK //从进程屏蔽里将信号删除。
SIG_SETMASK //将set的值设定为新的进程屏蔽。
set:为指向信号集的指针,在此专指新设的信号集,如果仅想读取现在的屏蔽值,可将其置为NULL。
oldset:也是指向信号集的指针,在此存放原来的信号集。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为EINVAL。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{
printf("Deal SIGINT"); //SIGINT信号处理函数
}
int main()
{
sigset_t newmask;
sigset_t oldmask;
sigset_t pendmask;
struct sigaction act;
act.sa_handler = handler; //handler为信号处理函数首地址
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, 0); //信号捕捉函数,捕捉Ctrl+C
sigemptyset(&newmask);//初始化信号量集
sigaddset(&newmask, SIGINT);//将SIGINT添加到信号量集中
sigprocmask(SIG_BLOCK, &newmask, &oldmask);//将newmask中的SIGINT阻塞掉,并保存当前信号屏蔽字到Oldmask
sleep (5);//休眠5秒钟,说明:在5s休眠期间,任何SIGINT信号都会被阻塞,如果在5s内收到任何键盘的Ctrl+C信号,则此时会把这些信息存在内核的队列中,等待5s结束后,可能要处理此信号。
sigpending(&pendmask);//检查信号是悬而未决的,
if (sigismember(&pendmask, SIGINT))//SIGINT是悬而未决的。所谓悬而未决,是指SIGINT被阻塞还没有被处理
{
printf("/nSIGINT pending/n");
}
sigprocmask(SIG_SETMASK, &oldmask, NULL);//恢复被屏蔽的信号SIGINT
//此处开始处理信号,调用信号处理函数
printf("SIGINT unblocked/n");
return (0);
}
注意:上面还有一种方式:
sigprocmask(SIG_BLOCK, &newmask, NULL); //阻塞
sigprocmask(SIG_UNBLOCK, &newmask, NULL);//取消阻塞
sigsuspend()函数作用详解
1)头文件:#include <signal.h>
2)一个保护临界区代码的错误实例:(sigprocmask()和pause()实现)
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig)
{
}
int main()
{
}
上面实例的问题是:本来期望pause()之后,来SIGINT信号,可以结束程序;可是,如果当“取消阻塞”和“pause”之间,正好来了SIGINT信号,结果程序因为pause的原因会一直挂起。。。
解决的方式,当然是sigsuspend()函数了。
3)使用sigsuspend()的程序
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig)
{
}
int main()
{
}
sigsuspend的原子操作是:
(1)设置新的mask阻塞当前进程(上面是用wait替换new,即阻塞SIGUSR1信号)
(2)收到SIGUSR1信号,阻塞,程序继续挂起;收到其他信号,恢复原先的mask(即包含SIGINT信号的)。
(3)调用该进程设置的信号处理函数(程序中如果先来SIGUSR1信号,然后过来SIGINT信号,则信号处理函数会调用两次,打印不同的内容。第一次打印SIGINT,第二次打印SIGUSR1,因为SIGUSR1是前面阻塞的)
(4)待信号处理函数返回,sigsuspend返回了。(sigsuspend将捕捉信号和信号处理函数集成到一起了)