Linux-C UDP简单例子
一、简述
记--使用UDP协议通信的简单例子。
说明: 网络中有很多主机,我们使用IP地址标识主机的身份。一台主机有多个应用程序,我们使用端口号来标识应用。
例子打包链接: https://pan.baidu.com/s/1-JlZpAd5A86Lkor03FbGZg 提取码: 6wrq
例子1:UDP单播,一方发送,一方接收。
例子2:UDP单播,使用多线程实现 双方通信。
例子3:UDP单播,使用多路复用实现 双方通信。
例子4:组播,recv程序加入组播,当其他应用往该组发送组播消息时,recv程序将收到其他应用发给组的消息。
例子5:广播,send往xxx端口发送广播消息,在同以网段中绑定xxx端口的recv程序就会收到广播消息。
二、例子
直接创建UDP通信socket,然后使用sendto()函数向指定的地址发送数据。
需要接受数据的话,要绑定IP端口地址。使用recvfrom()函数接收数据。
例子1:UDP单播,一方发送,一方接收。
效果:
send.c文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char * argv[])
{
//检查命令行参数是否匹配
if(argc != 3)
{
printf("请传递对方的ip和端口号");
return -1;
}
int port = atoi(argv[2]);//从命令行获取端口号
if( port<1025 || port>65535 )//0~1024一般给系统使用,一共可以分配到65535
{
printf("端口号范围应为1025~65535");
return -1;
}
//1 创建udp通信socket
int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if(udp_socket_fd == -1)
{
perror("socket failed!\n");
return -1;
}
//设置目的IP地址
struct sockaddr_in dest_addr = {0};
dest_addr.sin_family = AF_INET;//使用IPv4协议
dest_addr.sin_port = htons(port);//设置接收方端口号
dest_addr.sin_addr.s_addr = inet_addr(argv[1]); //设置接收方IP
char buf[1024] = {0};
//2 循环发送数据
while (1)
{
printf("Please input msg:");
scanf("%s",buf);//从键盘输入获取要发送的消息
sendto(udp_socket_fd, buf, strlen(buf), 0, (struct sockaddr *)&dest_addr,sizeof(dest_addr));
if(strcmp(buf, "exit") == 0)
{
break;//退出循环
}
memset(buf,0,sizeof(buf));//清空存留消息
}
//3 关闭通信socket
close(udp_socket_fd);
return 0;
}
recv.c文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
//判断命令行参数是否满足
if(argc != 2)
{
printf("请传递一个端口号\n");
return -1;
}
//将接收端口号并转换为int
int port = atoi(argv[1]);
if( port<1025 || port>65535 )//0~1024一般给系统使用,一共可以分配到65535
{
printf("端口号范围应为1025~65535");
return -1;
}
// 1.创建udp通信socket
int udp_socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(udp_socket_fd < 0 )
{
perror("creat socket fail\n");
return -1;
}
//2.设置UDP的地址并绑定
struct sockaddr_in local_addr = {0};
local_addr.sin_family = AF_INET; //使用IPv4协议
local_addr.sin_port = htons(port); //网络通信都使用大端格式
local_addr.sin_addr.s_addr = INADDR_ANY;//让系统检测本地网卡,自动绑定本地IP
int ret = bind(udp_socket_fd,(struct sockaddr*)&local_addr,sizeof(local_addr));
if(ret < 0)
{
perror("bind fail:");
close(udp_socket_fd);
return -1;
}
else
{
printf("recv ready!!!\n");
}
struct sockaddr_in src_addr = {0}; //用来存放对方(信息的发送方)的IP地址信息
int len = sizeof(src_addr); //地址信息的大小
char buf[1024] = {0};//消息缓冲区
//3 循环接收客户发送过来的数据
while(1)
{
ret = recvfrom(udp_socket_fd, buf, sizeof(buf), 0, (struct sockaddr *)&src_addr, &len);
if(ret == -1)
{
break;
}
printf("[%s:%d]",inet_ntoa(src_addr.sin_addr),ntohs(src_addr.sin_port));//打印消息发送方的ip与端口号
printf("buf=%s\n",buf);
if(strcmp(buf, "exit") == 0)
{
break;
}
memset(buf, 0, sizeof(buf));//清空存留消息
}
//4 关闭通信socket
close(udp_socket_fd);
}
例子2:UDP单播,使用多线程实现 双方通信。
udp.c文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
void * recv_msg(void *arg);//接收消息
int main(int argc,char *argv[])
{
//判断命令行参数是否满足
if(argc != 2)
{
printf("请传递一个端口号\n");
return -1;
}
//将接收端口号并转换为int
int port = atoi(argv[1]);
if( port<1025 || port>65535 )//0~1024一般给系统使用,一共可以分配到65535
{
printf("端口号范围应为1025~65535");
return -1;
}
// 1.创建udp通信socket
int udp_socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(udp_socket_fd < 0 )
{
perror("creat socket fail\n");
return -1;
}
//2.设置UDP的地址并绑定
struct sockaddr_in local_addr = {0};
local_addr.sin_family = AF_INET; //使用IPv4协议
local_addr.sin_port = htons(port); //网络通信都使用大端格式
local_addr.sin_addr.s_addr = INADDR_ANY;//让系统检测本地网卡,自动绑定本地IP
int ret = bind(udp_socket_fd,(struct sockaddr*)&local_addr,sizeof(local_addr));
if(ret < 0)
{
perror("bind fail:");
close(udp_socket_fd);
return -1;
}
//开启接收线程
pthread_t recv_thread;
pthread_create(&recv_thread, NULL, recv_msg, (void*)&udp_socket_fd);
//设置目的IP地址
struct sockaddr_in dest_addr = {0};
dest_addr.sin_family = AF_INET;//使用IPv4协议
int dest_port = 0;//目的端口号
char dest_ip[32] = {0};//目的IP
char msg[1024] = {0};
//循环发送消息
while(1)
{
printf("ip port msg\n");
scanf("%s %d %s", dest_ip, &dest_port, msg);//输入目的ip 与 端口号
dest_addr.sin_port = htons(dest_port);//设置接收方端口号
dest_addr.sin_addr.s_addr = inet_addr(dest_ip); //设置接收方IP
sendto(udp_socket_fd, msg, strlen(msg), 0, (struct sockaddr *)&dest_addr,sizeof(dest_addr));
if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)
{
pthread_cancel(recv_thread);//取消子线程
break;//退出循环
}
memset(msg,0,sizeof(msg));//清空存留消息
memset(dest_ip,0,sizeof(dest_ip));
}
//4 关闭通信socket
close(udp_socket_fd);
}
//接收线程所要执行的函数 接收消息
void * recv_msg(void *arg)
{
int ret = 0;
int *socket_fd = (int *)arg;//通信的socket
struct sockaddr_in src_addr = {0}; //用来存放对方(信息的发送方)的IP地址信息
int len = sizeof(src_addr); //地址信息的大小
char msg[1024] = {0};//消息缓冲区
//循环接收客户发送过来的数据
while(1)
{
ret = recvfrom(*socket_fd, msg, sizeof(msg), 0, (struct sockaddr *)&src_addr, &len);
if(ret == -1)
{
break;
}
printf("[%s:%d]",inet_ntoa(src_addr.sin_addr),ntohs(src_addr.sin_port));//打印消息发送方的ip与端口号
printf("msg=%s\n",msg);
if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)
{
//通知主线程。。。
break;
}
memset(msg, 0, sizeof(msg));//清空存留消息
}
//关闭通信socket
close(*socket_fd);
return NULL;
}
例子3:UDP单播,使用多路复用实现 双方通信。
效果:
udp.c文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
//判断命令行参数是否满足
if(argc != 2)
{
printf("请传递一个端口号\n");
return -1;
}
//将接收端口号并转换为int
int port = atoi(argv[1]);
if( port<1025 || port>65535 )//0~1024一般给系统使用,一共可以分配到65535
{
printf("端口号范围应为1025~65535");
return -1;
}
// 1.创建udp通信socket
int udp_socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(udp_socket_fd < 0 )
{
perror("creat socket fail\n");
return -1;
}
//2.设置UDP的地址并绑定
struct sockaddr_in local_addr = {0};
local_addr.sin_family = AF_INET; //使用IPv4协议
local_addr.sin_port = htons(port); //网络通信都使用大端格式
local_addr.sin_addr.s_addr = INADDR_ANY;//让系统检测本地网卡,自动绑定本地IP
int ret = bind(udp_socket_fd,(struct sockaddr*)&local_addr,sizeof(local_addr));
if(ret < 0)
{
perror("bind fail:");
close(udp_socket_fd);
return -1;
}
printf("you can recv and send!!!\n");
//定义一个文件描述符集合
fd_set fds;
//存放地址信息
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;//使用IPv4协议
int dest_port = 0;//目的端口号
char dest_ip[32] = {0};//目的IP
char msg[1024] = {0};
int len = sizeof(addr);//地址信息大小
//循环监视文件描述符集合
while(1)
{
//清空文件描述符集合
FD_ZERO(&fds);
//把标准输入设备加入到集合中
FD_SET(0,&fds);
//把网络通信文件描述符加入到集合中
FD_SET(udp_socket_fd, &fds);
ret = select(udp_socket_fd+1,&fds,NULL,NULL,NULL);//阻塞等待,直到集合中有活跃的描述符
if(ret < 0)//错误
{
perror("select fail:");
close(udp_socket_fd);
return -1;
}else if(ret > 0) //有活跃的 ret为1
{
//判断是否是 标准输入设备活跃 假设是则发送数据
if(FD_ISSET(0, &fds))//标准输入的描述符是0
{
printf("ip port msg\n");
scanf("%s %d %s", dest_ip, &dest_port, msg);//输入目的ip 与 端口号
addr.sin_port = htons(dest_port);//设置接收方端口号
addr.sin_addr.s_addr = inet_addr(dest_ip); //设置接收方IP
sendto(udp_socket_fd, msg, strlen(msg), 0, (struct sockaddr *)&addr,sizeof(addr));
if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)
{
break;//退出循环
}
memset(dest_ip, 0, sizeof(dest_ip));
}
//判断是否是new_socket_fd活跃,(有消息收到)
if(FD_ISSET(udp_socket_fd, &fds))
{
ret = recvfrom(udp_socket_fd, msg, sizeof(msg), 0, (struct sockaddr *)&addr, &len);
if(ret == -1)
{
break;
}
printf("[%s:%d]",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));//打印消息发送方的ip与端口号
printf("msg=%s\n",msg);
if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)
{
//通知主线程。。。
break;
}
}
memset(msg, 0, sizeof(msg));//清空存留消息
}
}
//4 关闭通信socket
close(udp_socket_fd);
}
2、UDP组播 (涉及多台主机)
加入某一个组,那么有成员往该组发送信息时,所有组员都将收到信息。往某个组播IP地址的xxx端口发送信息。那么所有加入该组的主机 中注册xxx端口的应用都将收到。
//1 创建UDP通信socket
int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
//2 加入组播
struct ip_mreq zu = {0};//存放组播地址信息
zu.imr_multiaddr.s_addr = inet_addr("224.10.10.2"); //设置组播地址224.0.0.0~239.255.255.255
zu.imr_interface.s_addr = inet_addr("0.0.0.0");
setsockopt(udp_socket_fd, IPPROTO_IP,I P_ADD_MEMBERSHIP, &zu, sizeof(zu);
//3 绑定地址 (可以通过绑定的IP端口接收其它应用发来的消息)
struct sockaddr_in local_addr ={0};
local_addr.sin_family = AF_INET; //使用IPv4协议
local_addr.sin_port = htons(port)); //网络通信都使用大端格式
local_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); //32位的整形 //注意:Linux下设置组播的时候绑定地址只能绑定0.0.0.0地址否则会接收不到数据
bind(udp_socket_fd,(struct sockaddr*)&local_addr,sizeof(local_addr));
//4 收发数据
char buf[1024] = {0};//数据缓区
//接收数据 (可以收到组播的消息,也可以收到绑定端口的消息)
struct sockaddr_in src_addr ={0}; //定义一个缓冲区 存放对方的IP地址信息
int len = sizeof(src_addr);//地址的大小
recvfrom(udp_socket_fd, buf,sizeof(buf), 0, (struct sockaddr *)&src_addr,&len);
//发送数据 (可以往组播地址发送,也可以往制定的IP端口发送,组播地址就是特殊的IP地址)
sendto(udp_socket_fd, "hello", strlen("hello"), 0, (struct sockaddr *)&zu_addr, sizeof(zu_addr));
//其中组播地址
struct sockaddr_in zu_addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = inet_addr("224.10.10.2"); //设置组播地址 D类地址
例子4:recv程序加入组播,当其他应用往该组发送组播消息时,recv程序将收到其他应用发给组的消息。
(同时也可以收到指定发给自己的消息)
效果:
recv.c文件
//接收组播消息
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
//命令行传参,默认系统会传递一个参数--可执行文件的名称,我们传递的第一个参数是argv[1]
//判断命令行参数是否满足
if(argc != 3)
{
printf("请传递组播ip,及要绑定的端口号\n");
return -1;
}
//将接收端口号并转换为int
int port = atoi(argv[2]);
if( port<1025 || port>65535 )//0~1024一般给系统使用,一共可以分配到65535
{
printf("端口号范围应为1025~65535");
return -1;
}
//1、 创建udp通信socket AF_INET:IPv4协议,SOCK_DGRAM:UDP数据报,0:默认属性
int udp_socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(udp_socket_fd < 0 )
{
perror("creat socket fail\n");
return -1;
}
//2、加入组播 组播地址224.0.0.0~239.255.255.255
struct ip_mreq zu = {0};
zu.imr_multiaddr.s_addr = inet_addr(argv[1]); //设置组播地址
zu.imr_interface.s_addr = inet_addr("0.0.0.0");
int ret = setsockopt(udp_socket_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &zu, sizeof(zu));
if(ret < 0)
{
perror("setsockopt fail\n");
return -1;
}
else
{
printf("已经加入组:[%s:%d], 等待接收组消息!!!\n", argv[1], port);
}
//3、设置UDP的地址并绑定
struct sockaddr_in local_addr = {0};//地址信息结构体
local_addr.sin_family = AF_INET; //使用IPv4协议
local_addr.sin_port = htons(port); //网络通信都使用大端格式
local_addr.sin_addr.s_addr = inet_addr("0.0.0.0"); //32位的整形
//注意:Linux下,加入组播后,绑定地址只能绑定0.0.0.0地址否则会接收不到数据
ret = bind(udp_socket_fd, (struct sockaddr*)&local_addr, sizeof(local_addr));
if(ret < 0)
{
perror("bind fail:");
return -1;
}
struct sockaddr_in src_addr={0}; //定义一个缓冲区 存放对方的IP地址信息
int len = sizeof(src_addr);
char buf[1024] = {0};//消息缓冲区
//4 接收消息
while(1)
{
recvfrom(udp_socket_fd, buf, sizeof(buf), 0, (struct sockaddr *)&src_addr, &len);
printf("[IP:%s,PORT:%d]",inet_ntoa(src_addr.sin_addr),ntohs(src_addr.sin_port));//打印消息发送者的IP信息
printf(" buf=%s\n", buf);//打印消息
if(strcmp(buf, "exit") == 0)
{
break;//当接收到"exit"时退出循环
}
bzero(buf, sizeof(buf));//清空接收到的消息,避免影响下次接收
}
}
send.c文件
//发送组播消息
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int arg, char * args[])
{
//1 创建UDP通信socket
int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_fd == -1)
{
printf("create socket failed ! error message :%s\n", strerror(errno));
return -1;
}
//地址信息
struct sockaddr_in dest_addr = {0};
dest_addr.sin_family = AF_INET;
int dest_port = 0;
char dest_ip[32] = {0};
char buf[1024] = {0};
//2 发送消息
while (1)
{
printf("ip port msg:");
scanf("%s %d %s", dest_ip, &dest_port, buf);
//设置目的IP端口
dest_addr.sin_port = htons(dest_port); //网络通信都使用大端格式
dest_addr.sin_addr.s_addr = inet_addr(dest_ip); //32位的整形
if (sendto(udp_socket_fd, buf, strlen(buf), 0, (struct sockaddr *) &dest_addr, sizeof(dest_addr)) == -1)
{
printf("sendto failed ! error message :%s\n", strerror(errno));
break;
}
if(strcmp(buf, "exit") == 0)
{
break;
}
memset(buf,0,sizeof(buf));
}
//3 关闭通信socket
close(udp_socket_fd);
return 0;
}
3、UDP广播 (涉及多台主机)
往某个IP网段的xxx端口发送信息。那么在本网段的所有主机中注册xxx端口的应用都将收到(包括自己)。
//1 创建UDP通信socket
int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
//2 开启发送广播数据功能
int on = 1; //1开启 0关闭
int ret = setsockopt(udp_socket_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
//3 向广播地址发送广播数据
sendto(udp_socket_fd, "hello", strlen("hello"), 0, (struct sockaddr *)&bro_addr,sizeof(bro_addr));
//其中bro_addr为广播地址
struct sockaddr_in bro_addr = {0};
bro_addr.sin_family = AF_INET;//IPv4协议
bro_addr.sin_port = htons(8888);//设置广播端口
bro_addr.sin_addr.s_addr = inet_addr("192.168.13.255"); //设置本网段广播地址,192.168.xxx.255
例子5:广播的发送与接收。send往xxx端口发送广播消息,在同以网段中绑定xxx端口的recv程序就会收到广播消息
某个应用往xxx端口发出广播,在同一网段的绑定xxx端口的所有应用都会收到广播的消息(如果发出广播消息的应用板绑定的也是xxx端口,也会收到广播消息)。如飞秋软件的好友上下线通知。
效果:
recv.c文件
//UDP 接收广播信息
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
//判断命令行参数是否满足
if(argc != 2)
{
printf("请传递一个端口号\n");
return -1;
}
//将接收端口号并转换为int
int port = atoi(argv[1]);
if( port<1025 || port>65535 )//0~1024一般给系统使用,一共可以分配到65535
{
printf("端口号范围应为1025~65535");
return -1;
}
// 1.创建udp通信socket
int udp_socket_fd = socket(AF_INET,SOCK_DGRAM,0);
if(udp_socket_fd < 0 )
{
perror("creat socket fail\n");
return -1;
}
//2.设置UDP的地址并绑定
struct sockaddr_in local_addr = {0};
local_addr.sin_family = AF_INET; //使用IPv4协议
local_addr.sin_port = htons(port); //网络通信都使用大端格式
local_addr.sin_addr.s_addr = INADDR_ANY;//inet_addr("0.0.0.0");;//Linux下,需要设为0.0.0.0才能接收到广播
int ret = bind(udp_socket_fd,(struct sockaddr*)&local_addr,sizeof(local_addr));
if(ret < 0)
{
perror("bind fail:");
close(udp_socket_fd);
return -1;
}
else
{
printf("recv ready!!!\n");
}
struct sockaddr_in src_addr = {0}; //用来存放对方(信息的发送方)的IP地址信息
int len = sizeof(src_addr); //地址信息的大小
char buf[1024] = {0};//消息缓冲区
//3 循环接收客户发送过来的数据
while(1)
{
ret = recvfrom(udp_socket_fd, buf, sizeof(buf), 0, (struct sockaddr *)&src_addr, &len);
if(ret == -1)
{
break;
}
printf("[%s:%d]",inet_ntoa(src_addr.sin_addr),ntohs(src_addr.sin_port));//打印消息发送方的ip与端口号
printf("buf=%s\n",buf);
if(strcmp(buf, "exit") == 0)
{
break;//收到"exit"退出循环
}
memset(buf, 0, sizeof(buf));//清空存留消息
}
//4 关闭通信socket
close(udp_socket_fd);
return 0;
}
send.c文件
//UDP 发送广播信息
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
//判断命令行参数是否满足
if(argc != 3)
{
printf("请传递广播ip,端口号\n");
return -1;
}
//接收端口号并转换为int
int port = atoi(argv[2]);
if( port<1025 || port>65535 )//0~1024一般给系统使用,一共可以分配到65535
{
printf("端口号范围应为1025~65535");
return -1;
}
//1.创建UDP socket
int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_fd == -1)
{
printf("create socket failed ! error message :%s\n", strerror(errno));
return -1;
}
//2.开启发送广播数据功能
int on = 1; //开启
int ret = setsockopt(udp_socket_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
if(ret < 0)
{
perror("setsockopt fail\n");
return -1;
}
//3.设置当前网段的广播地址
struct sockaddr_in bro_addr = {0};
bro_addr.sin_family = AF_INET;
bro_addr.sin_port = htons(port);
bro_addr.sin_addr.s_addr = inet_addr(argv[1]); //设置为广播地址
char buf[1024] = {0};//消息缓冲区
//4 发送数据
while(1)
{
printf("Please input broadcast msg:");
scanf("%s", buf);//获取要发送的消息
sendto(udp_socket_fd, buf, strlen(buf), 0, (struct sockaddr *)&bro_addr, sizeof(bro_addr));
if(strcmp(buf, "exit") == 0)
{
break;//退出循环
}
bzero(buf, sizeof(buf));
}
//5.关闭网络通信
close(udp_socket_fd);
return 0;
}
Ubuntu与开发板进行测试:
三、相关函数:
功能 | 创建通信端点(可以创建TCP通信端点、UDP通信端点) | |
头文件 | #include <sys/types.h> #include <sys/socket.h> | |
原型 | int socket(int domain, int type, int protocol); | |
参数 | domain | 通信协议簇(包含IPv4,IPv6),详细请看<sys/socket.h> |
type | 通信协议类型(包含TCP,UDP),更多请查询手册: man socket SOCK_STREAM:表示TCP SOCK_DGRAM:表示UDP | |
protocol | 指定与套接字一起使用的特定协议。通常,只有一个协议支持给定协议族中的特定套接字类型,在这种情况下协议可以指定为0.但是,可能存在许多协议,在这种情况下,必须在此指定特定协议方式。 | |
返回值 | 成功:返回一个文件描述符。 失败:返回-1,并设置errno | |
备注 | 详细请查看man手册:man 2 socket |
功能 | 将IP地址信息绑定到socket | |
头文件 | #include <sys/types.h> #include <sys/socket.h> | |
原型 | int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); | |
参数 | sockfd | 通信socket |
addr | 要绑定的地址信息(包括IP地址,端口号) 通用地址结构体定义: struct sockaddr { 新型的IP地址结构体:(查看新型的结构体信息: gedit /usr/include/linux/in.h ) struct sockaddr_in {
| |
addrlen | 地址信息大小 | |
返回值 | 成功:返回0。 失败:返回-1,并设置errno | |
备注 | 详细请查看man手册:man 2 bind |
功能 | 将无符号短整型数(unsigned short integer)主机字节顺序转换为网络字节序 (h:host主机,to,n:net网络,s:short短整型) | |
头文件 | #include <arpa/inet.h> | |
原型 | uint16_t htons(uint16_t hostshort); | |
参数 | hostshort | 无符号短整型数 |
返回值 | 转换后的值 | |
备注 | 详细请查看man手册:man 3 htons 相关函数: uint32_t htonl(uint32_t hostlong); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort); |
功能 | 将地址cp从IPv4数字和点符号(点分十进制形式)转换为 网络字节顺序的二进制形式。 (cp:char 类型的ip) | |
头文件 | #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> | |
原型 | in_addr_t inet_addr(const char *cp); | |
参数 | cp | ip地址 (如"192.168.21.155") |
返回值 | 成功:转换后的ip值 失败:INADDR_NONE (-1) | |
备注 | 手册中提到: 使用inet_addr()函数是有问题的,因为失败时返回的-1是有效地址(255.255.255.255),应该避免使用。可以使用inet_aton(),inet_pton(3)或getaddrinfo(3),它们提供了一种更清晰的方式来指示错误返回。 详细请查看man手册:man 3 inet_addr 相关函数: int inet_aton(const char *cp, struct in_addr *inp); in_addr_t inet_network(const char *cp); char *inet_ntoa(struct in_addr in); struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host); in_addr_t inet_lnaof(struct in_addr in); in_addr_t inet_netof(struct in_addr in); |
功能 | 发送消息给socket。(一般在UDP通信中使用,往指定的IP端口发送消息) | |
头文件 | #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 | 要发送的消息长度.(一般为sizeof(buf) ) | |
flags | 附加标志选项,UDP通信中为0即可。 MSG_CONFIRM:告诉对方收到之后回复确认。更多请查询man 手册:man 2 sendto | |
dest_addr | 目的地址。(也就是接收方的地址信息) | |
addrlen | 目的地址长度(一般为sizeof(dest_addr)) | |
返回值 | 转换后的值 | |
备注 | 详细请查看man手册:man 2 sendto |
功能 | 从socket接收消息。(一般在UDP通信中使用,注册某个IP端口,然后从此IP端口接收消息) | |
头文件 | #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,(已经绑定/注册 某个IP端口号的socket才可以从IP端口接收消息) |
buf | 用来存放接收到的消息 | |
len | 要接收的消息长度.(一般为sizeof(buf) ) | |
flags | 附加标志选项,UDP通信中为0即可。更多请查询man 手册:man 2 sendto | |
src_addr | 消息源地址。(用来保存发送方的地址信息) | |
addrlen | 消息源地址长度(一般为sizeofr(src_addr)) | |
返回值 | 转换后的值 | |
备注 | 详细请查看man手册:man 2 sendto |
功能 | 监视多个文件描述符,阻塞等待设定的时间,直到一个或多个文件描述符变为“准备好”,继续往下执行。 | |
头文件 | #include <sys/time.h> #include <sys/types.h> #include <unistd.h> | |
原型 | int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); | |
参数 | nfds | 要监视的描述符集合中数值最大的描述符 |
readfds | 读文件描述符集合,通常设置为要监视的描述符集合 | |
writefds | 写文件描述符集合,通常设置为NULL | |
exceptfds | 其它文件描述符集合,通常设置为NULL | |
timeout | 超时时间(阻塞等待的时间,时间一到,就会往下执行。如果为NULL,会一直等到有描述符活跃为止) 时间结构体 (定义在<sys/time.h> 头文件中) struct timeval { | |
返回值 | -1:出错 0:超时 1:有活跃的文件描述符 | |
备注 | 详细请查看man手册:man 2 select 相关函数: void FD_CLR(int fd, fd_set *set); //将某个描述符 从集合中清除出去 注:select会更新 超时时间timeout,所以如果多次使用select需要重置timeout。 往下执行时,将修改每个文件描述符集以指示哪些文件描述符实际更改了状态。 因此,如果在循环中使用select(),则需要在每次调用之前对集合进行重新定义。 |
功能 | 在套接字上设置选项(设置socket套接字的相关操作、属性) | |
头文件 | #include <sys/types.h> #include <sys/socket.h> | |
原型 | int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); | |
参数 | sockfd | 要设置的socket套接字 |
level | 选项所属的级别(不同的级别有不同的选项-不同的操作) | |
optname | 要设置的选项名称 | |
optval | 指向标志变量,将设置给optname所指定的选项。 | |
optlen | optval指向的变量所占的字节数 | |
返回值 | 成功:返回0 出错:返回-1,并设置error | |
备注 | 详细请查看man手册:man 2 setsockopt 示例: 开启发送广播数据功能 |
套接字设置选项:
Leve(级别) | optname(选项名) | 说明 | 数据类型 |
SOL_SOCKET
| SO_BROADCAST | 允许发送广播数据报 | int |
SO_DEBUG | 开启调试跟踪 | int | |
SO_DONTROUTE | 绕过外出路由表查询 | int | |
SO_KEEPALIVE | 周期性测试连接是否仍存活 | int | |
SO_LINGER | 若有数据待发送则延迟关闭 | linger | |
SO_OOBINLINE | 让接收到的带外数据继续在线留存 | int | |
SO_RCVBUF | 接收缓冲区大小 | int | |
SO_SNDBUF | 发送缓冲区大小 | int | |
SO_RCVLOWAT | 接收缓冲区低水位标记 | int | |
SO_SNDLOWAT | 发送缓冲区低水位标记 | int | |
SO_RCVTIMEO | 接收超时 | timeval{} | |
SO_SNDTIMEO | 发送超时 | timeval{} | |
SO_REUSEADDR | 允许重用本地地址 | int | |
SO_REUSEPORT | 允许重用本地端口 | int | |
SO_TYPE | 取得套接字类型 | int | |
SO_USELOOPBACK | 路由套接字取得所发送数据的副本 | int | |
IPPROTO_IP
| IP_HDRINCL | 随数据包含的IP首部 | int |
IP_OPTIONS | IP首部选项 | int | |
IP_RECVDSTADDR | 返回目的IP地址 | int | |
IP_RECVIF | 返回接收接口索引 | int | |
IP_TOS | 服务类型和优先权 | int | |
IP_TTL | 存活时间 | int | |
IP_MULTICAST_IF | 指定外出接口 | in_addr{} | |
IP_MULTICAST_TTL | 指定外出TTL | u-char | |
IP_MULTICAST_LOOP | 指定是否环回 | u_char | |
IP_ADD_MEMBERSHIP | 加入多播组 | ip_mreq{} | |
IP_DROP_MEMBERSHIP | 离开多播组 | ip_mreq{} | |
IP_BLOCK_SOURCE | 阻塞多组播 | ip_mresource{} | |
IP_UNBLOCK_SOURCE | 开通多组播 | ip_mresource{} | |
IP_ADD_SOURCE_MEMBERSHIP | 加入源特定多组播 | ip_mresource{} | |
IP_DROP_SOURCE_MEMBERSHIP | 离开源特定多组播 | ip_mresource{} | |
IPPROTO_ICMPV6 | ICMP6_FILTIER | 指定待传递的ICMPv6 | icmp6_filter{} |
IPPROTO_IPV6
| IPV6_CHECKSUM | 用于原始套接字的校验和字段偏移 | int |
IPV6_DONTFRAG | 丢弃大的分组而将其分片 | int | |
IPV6_NEXTHOP | 指定下一跳地址 | sockaddr_int6{} | |
IPV6_PATHMTU | 获取当前路径MTU | ip6_mtuinfo{}_ | |
IPV6_RECVDSTOPTS | 接收目的地选项 | int | |
IPV6_RECVHOPLIMIT | 接收单播跳限 | int | |
IPV6_RECVHOPOPTS | 接收步跳选项 | int | |
IPV6_RECVPATHMTU | 接收路径MTU | int | |
IPV6_RECVPKTINFO | 接收分组信息 | int | |
IPV6_CVRTHDR | 接收源路径 | int | |
IPV6_RECVTCLASS | 接收流通类型 | int | |
IPV6_UNICAST_HOPS | 默认单播跳限 | int | |
IPV6_USE_MIN_MTU | 使用最小MTU | int | |
IPV6_V6ONLY | 禁止v4兼容 | int | |
IPV6_XXX | 粘附性辅助数据 | int | |
IPV6_ULTICAST_IF | 指定外出接口 | u_int | |
IPV6_MULTICAST_HOPS | 指定外出跳限 | int | |
IPV6_MULTICAST_LOOP | 指定是否环回 | u_nt | |
IPV6_JOIN_GROUP | 加入多组播 | ip6_mreq{} | |
IPV6_LEAVE_GROUP | 离开多组播 | ip6_mreq{} | |
IPPROTO_IP 或PROTO_IPV6
| MCAST_JOIN_IF | 加入多组播 | group_req{} |
MCAST_LEAVE_GROUP | 离开多组播 | group_source_req{} | |
MCAST_BLOCK_SOURCE | 阻塞多播源 | group_source_req{} | |
MCAST_UNBLOCK_SOURCE | 开通多播源 | group_source_req{} | |
MCAST_JOIN_SOURCE_GROUP | 加入源特定多播组 | group_source_req{} | |
MCAST_LEAVE_SOURCE_GROUP | 离开源特定多播组 | group_source_req{} | |
IPPROTO_TCP | TCP_MAXSEG | TCP最大分节大小 | int |
TCP_NODELAY | 禁止Nagle算法 | int | |
IPPROTO_SCTP | SCTP_ADAPTION_LAYER | 适配层指示 | sctp_setadaption{} |
SCTP_ASSOCINFO | 检查并设置关联信息 | sctp_assocparams{} | |
SCTP_AUTOCLOSE | 自动关闭操作 | int | |
SCTP_DEFAULT_SEND_PARAM | 默认发送参数 | sctp_sndrcvinfo{} | |
SCTP_DISABLE_FRAGMENTS | SCTP分片 | int | |
SCTP_EVENTS | 感兴趣事件的通知 | sctp_event_subscribe{} | |
SCTP_I_WANT_MAPPED_V4_ADDR | 映射的v4地址 | int | |
SCTP_INITMSG | 默认的INIT参数 | sctp_initmsg{} | |
SCTP_MAXBURST | 最大猝发大小 | int | |
SCTP_MAXSEG | 最大分片大小 | int | |
SCTP_NODELAY | 禁止Nagle算法 | int | |
SCTO_PEER_ADDR_PARAMS | 对端地址参数 | sctp_paddrparams{} | |
SCTP_PRIMARY_ADDR | 主目的地址 | sctp_setprim{} | |
SCTP_PTOINFO | RTO信息 | sctp_rtoinfo{} | |
SCTP_SET_PEER_PRIMARY_ADDR | 对端的主目的地址 | sctp_setpeerprim{} |