目录
一、UDP
无连接;
多个客户端可以发送消息给服务器
1. 流程
服务器:
socket() -> bind() -> recvfrom()/sendto() -> close() UDP客户端流程: socket() -> recvfrom()/sendto() -> close()
客户端:
socket() -> recvfrom()/sendto() -> close()
2. 相关函数
(1)recvfrom
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收数据
参数:
sockfd:socket函数的返回值 数据报套接字
buf:内存地址
len:接收数据的大小
flags:0
src_addr:发送端的结构体地址
addrlen:发送端的结构体长度的地址
返回值:
成功返回接受的字节数,失败返回-1
(2) sendto()
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
功能:
参数:
sockfd:socket函数的返回值 数据报套接字
buf:内存地址
len:接收数据的大小
flags:0
dest_addr:发送端的结构体地址
addrlen:发送端的结构体长度的地址
返回值:
成功返回接受的字节数,失败返回-1
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n", sockfd);
struct sockaddr_in serveraddr,clientaddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(7777);
serveraddr.sin_addr.s_addr = inet_addr("0");
int len = sizeof(serveraddr);
int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len);
if(ret == -1)
{
perror("bind");
return -1;
}
//收发数据
char buf[64] = {0};
int m = sizeof(clientaddr);
while(1)
{
int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &m );
if(n == -1)
{
perror("recvfrom");
return -1;
}
printf("n=%d\n", n);
printf("client ip:%s client port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
printf("message:%s\n", buf);
memset(buf, 0 ,sizeof(buf));
}
close(sockfd);
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
if(argc!=2)
{
printf("%s ip\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n", sockfd);
struct sockaddr_in serveraddr,clientaddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(7777);
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
//收发数据
char buf[64] = {0};
int len = sizeof(serveraddr);
while(1)
{
gets(buf);
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serveraddr, len);
memset(buf, 0 ,sizeof(buf));
}
close(sockfd);
return 0;
}
设置套接字属性 --- setsockopt
int setsockopt(int sockfd, int level, int optname,void *optval, socklen_t optlen);
属性设置表:level
SOL_SOCKET --- 广播
------------------------------------------------
参数 optname 宏的作用 对应参数optaval的类型
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSEADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD 系统兼容 int ======================================================
IPPROTO_IP --- 组播
------------------------------------------------------
IP_ADD_MEMBERSHIP 加入到组播组中 struct ip_mreq
IP_MULTICAST_IF 允许开启组播报文的接口 struct ip_mreq
二、广播
1.概念
同时发给局域网中的所有主机,称为广播;
只有用户数据报(使用UDP协议套接字)才能广播;广播地址:
以192.168.1.0 (255.255.255.0)网段为例,最大的主机地址192.168.1.255代表该网段的广播地址发到该地址的数据包被所有的主机接收
255.255.255.255在所有网段中都代表广播地址
2. 流程
发送广播消息:
1.socket(AF_INET, SOCK_DGRAM, 0); //创建用户数据报UDP套接字
2.struct sockaddr_in argv[1] //向广播地址发送
3.setsockopt //设置套接字属性,开广播权限
4.sendto (如果不开权限,会报错)
接收广播消息:
1.socket(AF_INET, SOCK_DGRAM, 0);
2.struct sockaddr_in serveraddr;
3.bind //"0"地址 or 广播地址(只能收广播消息)
4.recvfrom
发送端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n", sockfd);
struct sockaddr_in serveraddr,clientaddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(7777);//端口号
serveraddr.sin_addr.s_addr = inet_addr("0");//自动路由
int len = sizeof(serveraddr);
int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len);
if(ret == -1)
{
perror("bind");
return -1;
}
//收发数据,没有连接,相对与TCP
char buf[64] = {0};
int m = sizeof(clientaddr);
while(1)//不停的接受数据,客户端关掉,对服务端没有影响,因为没有连接
{
int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &m );
if(n == -1)
{
perror("recvfrom");
return -1;
}
printf("n=%d\n", n);
printf("client ip:%s client port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
printf("message:%s\n", buf);
memset(buf, 0 ,sizeof(buf));
}
close(sockfd);
return 0;
}
接收端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
int main(int argc, const char *argv[])
{
if(argc!=2)
{
printf("%s ip\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n", sockfd);
//打开广播权限
int on = 1;
int m = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) );//允许发送广播权限
if(m == -1)
{
perror("setsockopt");
return -1;
}
struct sockaddr_in serveraddr,clientaddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(7777);
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
//发数据
char buf[64] = {0};
int len = sizeof(serveraddr);
while(1)
{
gets(buf);
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serveraddr, len);
memset(buf, 0 ,sizeof(buf));
}
close(sockfd);
return 0;
}
三、组播
1. 特点
组播(又称为多播)是一种折中的方式,只有加入某个多播组的主机才能收到数据。
既可以发送多个主机, 又避免像广播一样带来过多的负载(过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信);
范围:224.0.0.1~239.255.255(D类网络地址)(1110 0000 -224 1110 1111-239)
2. 流程
发送组播消息:(client)
1.socket //创建用户数据报套接字
2.struct sockaddr_in addr //向组播地址发送,指定组播地址
3.sendto(); //发送数据包
接收组播消息:(server) man 7 ip
1.socket(); //创建用户数据报套接字
2.struct sockaddr_in serveraddr;
3.struct ip_mreqn mreq; // 组播地址 + "0"地址
4.setsockopt //加入多播组
5.bind(); //绑定ip地址和端口
6.recvfrom();
相关结构体:
struct ip_mreqn {
struct in_addr imr_multiaddr; /* IP多播组地址*/
struct in_addr imr_address; /* 本地接口的IP地址 */
int imr_ifindex; /* 接口索引 ip_mreq 无该字段 */
};
接收端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n", sockfd);
struct sockaddr_in serveraddr,clientaddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(7777);
serveraddr.sin_addr.s_addr = inet_addr("0");
int len = sizeof(serveraddr);
int ret = bind(sockfd, (struct sockaddr *)&serveraddr, len);
if(ret == -1)
{
perror("bind");
return -1;
}
struct ip_mreqn mrep;
mrep.imr_multiaddr.s_addr = inet_addr("224.10.10.10");//组播地址
mrep.imr_address.s_addr = inet_addr("0");//可以用0来表示
//加入到组播组中
ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mrep, sizeof(mrep) );
if(ret == -1)
{
perror("setsockopt");
return -1;
}
//收发数据
char buf[64] = {0};
int m = sizeof(clientaddr);
while(1)
{
int n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&clientaddr, &m );
if(n == -1)
{
perror("recvfrom");
return -1;
}
printf("n=%d\n", n);
printf("client ip:%s client port:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
printf("message:%s\n", buf);
memset(buf, 0 ,sizeof(buf));
}
close(sockfd);
return 0;
}
发送端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
if(argc!=2)
{
printf("%s ip\n", argv[0]);
return -1;
}
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("socket");
return -1;
}
printf("sockfd=%d\n", sockfd);
struct sockaddr_in serveraddr,clientaddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(7777);
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
//收发数据
char buf[64] = {0};
int len = sizeof(serveraddr);
while(1)
{
gets(buf);
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&serveraddr, len);
memset(buf, 0 ,sizeof(buf));
}
close(sockfd);
return 0;
}