学习笔记,小白可以相互学习,大佬看到能告诉咱理解不对的地方就好了。
UDP服务器流程:
1.socket
2.bind
3.具体操作(write/read/recvfrom/sebdto)
UDP客户端流程:
1.socket
2.bind(可选)
3.具体操作(write/read/recvfrom/sebdto)
/******client.c************************************/
#include
#include
#include
#include
#include
#include
int main()
{
int ret;
int sockfd;
char buf[256];
struct sockaddr_in srvaddr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return -1;
}
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.100");
while(1) {
fgets(buf, sizeof(buf), stdin);
ret = sendto(sockfd, buf, sizeof(buf), 0, (const struct sockaddr *)&srvaddr, sizeof(struct sockaddr));
if (ret == -1) {
perror("sendto");
return -1;
}
memset(buf, 0, sizeof(buf));
ret = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
if (ret == -1) {
perror("recvfrom");
return -1;
}
printf("buf : %s\n", buf);
}
return 0;
}
/*****server.c*************************************/
#include
#include
#include
#include
#include
#include
int main()
{
int ret;
int sockfd;
char buf[256];
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
socklen_t addrlen;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return -1;
}
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(sockfd, (const struct sockaddr *)&srvaddr, sizeof(struct sockaddr));
if (ret == -1 ) {
perror("bind");
return -1;
}
while(1) {
memset(buf, 0, sizeof(buf));
addrlen = sizeof(struct sockaddr);
ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&cltaddr, &addrlen);
if (ret == -1) {
perror("recvfrom");
return -1;
}
printf("buf : %s\n", buf);
sleep(10);
ret = sendto(sockfd, buf, sizeof(buf), 0, (const struct sockaddr *)&cltaddr, addrlen);
if (ret == -1) {
perror("sendto");
return -1;
}
}
return 0;
}
网络信息检索函数:
1.gethostname()获得主机名
2.getpeername()获得与套接口相连的远程协议地址
3.getsockname()获得本地套接口协议地址
4.gethostbyname()根据主机名取得主机信息
5.gethostbyaddr()根据主机地址取得主机信息
6.getprotobyname()根据协议名取得主机协议信息
7.getprotrbynumber()根据协议号取得主机协议信息
8.getserverbyname()根据服务名取得相关服务信息
9.getserverbyport()根据端口号取得相关服务信息
10.getsockopt()/setsockopt()获取/设置一个套接口选项
11.ioctl()/fcntl()设置套接口工作方式
网络超时检测
1.设置socket的属性SO_RCVTIMEO
参考代码:
struct tumeval tv;
tv.tv_sec = 5;//设置5秒时间,单位s
tv.t._usec = 0;//设置时间0ms,单位ms
setsockopt(sockfd,SOL_RCVTINMEO,&tv,sizeof(tv));//设置接收超时
recv()/recvfrom()/write()/read;//具体操作...,从socket读取或者接收数据
2.用select检测socket是否‘ready’
参考代码:
struct fd_set radfs;
struct timeval tv = {5,0};//设置5秒时间
FD_ZERO(&rdfs);
FD_SET(sockfd,&rdfs);
if(select(sockfd+1,&rdfs,NULL,NULL,&tv) > 0)//socket就绪
{
recv()/recvfrom()/write()/read;//具体操作...,从socket读取或者接收数据
}
3.设置定时器(timer),捕捉SIGALRM信号
参考代码:
void handler()int signo {return ;}//一旦进程收到这个信号,执行完信号处理函数之后,下一个函数就会直接返回,不阻塞在哪里
struct sigaction act;
sigaction(SIGALRM.NULL,&act);
act.sa_handler = handler;
act.sa_flags &= ~SA_RESTART;
sigaction(SIGALRM,&act,NULL);
alarm(5);
if(recv(...) < 0).....
广播(broadcast)
前面介绍的数据包发送方式只有一个接受方,称为单播。
如果同时发给局域网中的所有主机,称为广播。
只有用户数据报(使用UDP协议)套接字才能广播。
int setsockopt(int sockfd,int level,i
nt optname,const void *optval,socklen_t optelen)
level:指定控制套接字的层次可以取三种值:
1.SOL_SOCKET通用套接字选项 2.IPPROTO_IP: ip选项 3.IPPROTO_TCP: tcp选项
optname:存放选项值的缓冲区首地址
optlen:缓冲区首地址
optval:获得或者设置套接字选项,根据选项名称的数据类型进行转换。
返回值:成功:0,失败:-1,并设置errno
广播发送:
1.创建用户数据报套接字
2.缺省创建的套接字不允许广播数据包,需要设置属性(setsockopt可以设置套接字属性)
3.接收方地址指定为广播地址(a.指定端口信息 b.发送数据包)
广播接收:
1.创建用户数据报套接字
2.绑定IP地址(广播IP或者0.0.0.0)和端口
3.等待接收数据
/*******server.c************************************/
#include
#include
#include
#include
#include
#include
int main()
{
int ret;
int sockfd;
char buf[256];
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
socklen_t addrlen;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return -1;
}
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.255");
ret = bind(sockfd, (const struct sockaddr *)&srvaddr, sizeof(struct sockaddr));
if (ret == -1 ) {
perror("bind");
return -1;
}
while(1) {
memset(buf, 0, sizeof(buf));
addrlen = sizeof(struct sockaddr);
ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&cltaddr, &addrlen);
if (ret == -1) {
perror("recvfrom");
return -1;
}
printf("buf : %s\n", buf);
}
return 0;
}
/*******client.c*************************************/
#include
#include
#include
#include
#include
#include
int main()
{
int ret;
int sockfd;
char buf[256];
struct sockaddr_in srvaddr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return -1;
}
/* 允许发送广播 */
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("192.168.2.255");
while(1) {
fgets(buf, sizeof(buf), stdin);
ret = sendto(sockfd, buf, sizeof(buf), 0, (const struct sockaddr *)&srvaddr, sizeof(struct sockaddr));
if (ret == -1) {
perror("sendto");
return -1;
}
}
return 0;
}
单播方式只能发给一个接收方
广播方式发送给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常通信
组播(也叫多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据
网络地址的D类地址是组播地址:不分网络地址和主机地址,第一字节的前4位固定为1110,224.0.0.1~239.255.255.255
组播发送:
1.创建用户数据报套接字
2.接收方地址指定为组播地址
3.指定端口信息
4.发送数据包
组播接收:
1.创建用户数据报套接字
2.加入组播组
3.绑定IP地址(加入的组的组IP或者0.0.0.0)和端口
4.等待接收数据
/*****server.c************************************/
#include
#include
#include
#include
#include
#include
int main()
{
int ret;
int sockfd;
char buf[256];
struct sockaddr_in srvaddr;
struct sockaddr_in cltaddr;
socklen_t addrlen;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return -1;
}
//加入组播组
struct ip_mreqn mrq;
memset(&mrq, 0, sizeof(mrq));
mrq.imr_multiaddr.s_addr = inet_addr("224.10.10.1");
mrq.imr_address.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mrq, sizeof(mrq));
//bind
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(sockfd, (const struct sockaddr *)&srvaddr, sizeof(struct sockaddr));
if (ret == -1 ) {
perror("bind");
return -1;
}
printf("dsdgfsdg\n");
while(1) {
memset(buf, 0, sizeof(buf));
addrlen = sizeof(struct sockaddr);
ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&cltaddr, &addrlen);
if (ret == -1) {
perror("recvfrom");
return -1;
}
printf("buf : %s\n", buf);
}
return 0;
}
/*****client.c**************************************/
#include
#include
#include
#include
#include
#include
int main()
{
int ret;
int sockfd;
char buf[256];
struct sockaddr_in srvaddr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
return -1;
}
memset(&srvaddr, 0, sizeof(struct sockaddr_in));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(9999);
srvaddr.sin_addr.s_addr = inet_addr("224.10.10.1");
while(1) {
fgets(buf, sizeof(buf), stdin);
ret = sendto(sockfd, buf, sizeof(buf), 0, (const struct sockaddr *)&srvaddr, sizeof(struct sockaddr));
if (ret == -1) {
perror("sendto");
return -1;
}
printf("ret = %d\n", ret);
}
return 0;
}