网络属性设置函数
getsockopt() 和 setsockopt()
#include <sys/types.h>#include <sys/socket.h>int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
参数:
sockfd :
获取哪个文件描述的属性;
level :
层次和下面
optname
对应
SOL_SOCKET : 通用套接字选项
IPPROTO_IP :
IP
选项
IPPROTO_TCP :
TCP
选项
optname
:选项
SOL_SOCKET :=> level
SO_BROADCAST允许发送广播数据 int
SO_RCVTIMEO接收超时 struct timeval
SO_REUSEADDR允许重用本地地址和端口 int
IPPROTO_IP :=> level
IP_ADD_MEMBERSHIP 将 ip
添加到组
struct ip_mreq
IPPROTO_TCP :=> level
TCP_NODELAY 不使用 Nagle
算法
int
optval
:选项待设置的值
optlen :
选项值的空间大小
举例:
设置允许重用本地地址和端口
int on=1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
level = SOL_SOCKET
层次
对应选项是:
optname = SO_REUSEADDR 允许重用本地地址和端口
optval = &on
optlen = sizeof(on)
设置允许发送广播数据
int on=1
setsockopt(fd,SOL_SOCKET, SO_BROADCAST,&on,sizeof(on))
设置套接字的超时时间
(
阻塞在
recv,
超时返回
)。
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
struct timeval tv={5,0};
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv));
将 ip 添加到多播组
将
ip
添加到组
struct ip_mreq
struct ip_mreq iq;
iq.imr_multiaddr.s_addr=inet_addr("234.234.234.234"); //224-239
iq.imr_interface.s_addr=inet_addr("192.168.10.251"); //自己 ip 加入组播 ip
setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,&iq,sizeof(iq));
struct ip_mreq {
struct in_addr imr_multiaddr; /* IP multicast address of group 多播组的地址 */
struct in_addr imr_interface; /* local IP address of interface 本地 ip 地址 */
};
查找
zhongl@ubuntu:/usr/include$ grep -nir "struct ip_mreq" *
linux/in.h:166:struct ip_mreq {
..
zhongl@ubuntu:/usr/include$ vi linux/in.h +166
zhongl@ubuntu:/usr/include$
getsockopt()用法
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <pthread.h>
void *send_mesg(void *arg)
{
int newfd=*((int *)arg);
char buf[50]={};
while(1){
bzero(buf,50);
printf("pls input your message:\n");
scanf("%s",buf);
send(newfd,buf,strlen(buf),0);
}
}
int main(void)
{
int fd=socket(AF_INET,SOCK_STREAM,0);
if(fd==-1){
perror("socket");
}
int on;
int len=sizeof(on);
getsockopt(fd, SOL_SOCKET,SO_REUSEADDR,&on,&len);// on=0 刚开始没有
printf("on=%d\n",on);
printf("len=%d\n",len);
on=1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
getsockopt(fd, SOL_SOCKET,SO_REUSEADDR,&on,&len); //on=1 现在有了
printf("on=%d\n",on);
printf("len=%d\n",len);
struct sockaddr_in server;
server.sin_family=AF_INET;
server.sin_port=htons(10001);
//server.sin_addr.s_addr=inet_addr("192.168.7.3");
inet_aton("192.168.0.251", &server.sin_addr);
int ret=bind(fd,(struct sockaddr *)(&server),sizeof(server));
if(ret==-1){
perror("bind");
}
listen(fd,5);
struct sockaddr_in client;
len=sizeof(client);
int newfd=accept(fd,(struct sockaddr *)(&client),&len);
//运行服务器代码,程序阻塞在这个位置
printf("ip=%s\n",inet_ntoa(client.sin_addr));
printf("port=%d\n",ntohs(client.sin_port));
//创建线程
pthread_t tid;
pthread_create(&tid,NULL,send_mesg,&newfd);//传入文件描述符
char buf[50]={};
while(1){
bzero(buf,50);
ret=recv(newfd,buf,sizeof(buf),0);
if(ret==0){
break;
}
printf("server recv mess:%s\n",buf);
}
close(newfd);
close(fd);
return 0;
}
结果
zhongl@ubuntu:~/net/3day$ gcc getsockopt_server.c -o getsockopt_server -lpthread
zhongl@ubuntu:~/net/3day$ ./getsockopt_server
on=0
len=4
on=1
len=4
网络超时检查的几种方法
select()
struct timeval tv={5,0}; //5 秒select(sofd+1,&rdfs,NULL,NULL,&tv);
setsockopt()
struct timeval tv={5,0};setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));
设置定时器捕捉
SIGALRM
信号
void handler(int signo){return;}// 信号处理函数struct sigaction act;sigaction(SIGALRM,NULL,&act);// 注册闹钟信号act.sa_handler=handler;// 设置信号处理函数act.sa_flag&=~SA_RESTART;// 设置自重启属性为假sigaction(SIGALRM,&act,NULL);// 信号处理函数alarm(5);// 定时器设置 5 秒钟if(recv()<0)...
广播简介
数据包发送方式只有一个接受方,称为单播;
如果同时发给局域网中的所有主机,称为广播;只有用户数据报(
使用
UDP
协议
)
套接字才能广播;
广播地址以 192.168.1.0 (255.255.255.0)
网段为例,最大的主机地址
192.168.1.255
代表该网段的广播地址,发到该地址的数据包被所有的主机接收,255.255.255.255 在所有网段中都代表广播地址。
255.255.255.255
在所有网段中都代表广播地。
广播实现步骤
广播发送端
1>
创建用户数据报套接字;
2>
设置套接字为可以广播;
缺省创建的套接字不允许广播数据包,需要设置属性
setsockopt
可以设置套接字属性
3 >sendto()
到广播地址;
3.1
接收方地址指定为广播地址
3.2
指定端口信息
3.3
发送数据包
伪代码
sockfd = socket(,,);...int on = 1;setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));...sendto();
广播接收端
1>
创建用户数据报套接字;
2>
绑定地址;(广播
IP/
或
INADDR_ANY /0.0.0.0
)
绑定的端口必须和发送方指定的端口相同
//#define INADDR_ANY ((unsigned long int) 0x00000000)inet_addr("192.168.0.255");htonl(INADDR_ANY); // 用宏是长整型inet_addr("0.0.0.0") // 宏实际转换为 "0.0.0.0"查找ron@ubuntu:/usr/include$ grep -nir "INADDR_ANY" *linux/in.h:273:#define INADDR_ANY ((unsigned long int) 0x00000000)接收端可以使用3种://recv.sin_addr.s_addr=inet_addr("192.168.0.255");//recv.sin_addr.s_addr=inet_addr("0.0.0.0");recv.sin_addr.s_addr=htonl(INADDR_ANY);
3>
等待接收数据
recvfrom();
组播简介
单播方式只能发给一个接收方。
广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
组播
(
又称为多播
)
是一种折中的方式。只有加入某个多播组的主机才能收到数据。
多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载
(
每台主机要到传输层才能判断广播包
是否要处理
)
D
类
ip
用于组播
224 - 239 UDP
通信
组播实现步骤
组播发送端:
1
创建用户数据报套接字;
2
设置为广播;
3
接收方地址指定为组播地址
4
指定端口信息
5sendto
发送数据包到组播地址;
组播接收端:
1
创建用户数据报套接字;
2
加入多播组;
3
绑定
IP
与端口;(加入组的组
IP/INADDR_ANY/0.0.0.0
)
4
绑定的端口必须和发送方指定的端口相同
5recvfrom();
等待接收数据
广播,组播小结
1.
广播
发送端
接收端
socket socket
允许发送广播数据
允许端口重复使用
bind() ---------------->
广播
IP
sendto(
对着广播
IP) recvfrom
close close
2.
组播
发送端
接收端
socket socket
允许发送广播数据
允许端口重复使用
bind() ---------------->
组播
IP
自身
IP
加入到组播组
sendto(
对着组播
IP) recvfrom
close close
UNIX 域套接字
什么是 UNIX 域套接字
网络通信:
跨主机间的进程间通信;
进程间通信方式也包括
unix
域套接字;
unix
域套接字:
用于本地通信;
创建套接字时使用本地协议 AF_UNIX 分为流式套接字和用户数据报套接字;
和其他进程间通信方式相比使用方便、效率更高,常用于前后台进程通信。
使用 UNIX 域套接字
调用方法基本上和普通
TCP/IP
的套接字一样,只是有些许差别。
UNIX
域套接字在一台机器上的两个不同进程间通信,还要用到
IP
地址就有点大材小用了。
UNIX
域套接字仅仅复制数据;它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。
使用本地协议;
AF_UNIX
bind
绑定的结构体为
struct sockaddr_un{sa_family_t sun_family; // 协议类型char sun_path[108]; // 套接字文件路径}
会在路径下创建一个套接字文件,若已存在,先删除
添加结构体头文件
<sys/un.h>
填充地址结
struct sockaddr_un myaddr;bzero(&myaddr, sizeof(myaddr));myaddr.sun_family = AF_UNIX;strcpy(myaddr.sun_path, "mysocket");
UNIX 域双向通信,UDP 要两个 socket 文件
UNIX 域(流式)套接字
实际上,生成了一个叫
socket
的文件,类似于有名管道
1.
除了地址类型不同外,其他和
TCP
套接字的使用方法完全一样
2.
绑定套接字文件时,会自动创建该文件。若文件已存在则无法绑定。可以在调用
bind
前先用
remove
删除
3.
若客户端没有绑定地址
(
套接字文件
)
,系统不会自动分配
4.
客户端即使没有绑定地址,依然可以从服务器端收数据
(
因为套接字是面向连接的
)
UNIX 域(用户数据报)套接字
1.
除了地址类型不同外,其他和
UDP
套接字的使用方法完全一样
2.
绑定套接字文件时,会自动创建该文件。若文件已存在则无法绑定。可以在调用
bind
前先用
remove
删除
3.
若客户端没有绑定地址
(
套接字文件
)
,系统不会自动分配
4.
客户端没有绑定地址,只能发送数据,不能接收数据
5.
由于客户端没有建立连接,所以在双向通信时,需要两个文件
删除文件的函数
remove()
一般用这个
unlink()
常用进程通信
无名管道,有名管道,信号,信号量,共享内存,消息队列,线程信号量,
unix
域
------>
同主机
tcp
,
udp ------>
跨主机