本章接上章Socket编程(二)十、 网络参数的设置和获取获得主机名存到 hostname中
int gethostname(char *hostname, size_t size);
取得本地的SOCKET 信息
int getsockname(int sockfd, struct sockaddr *addr, int *addrlen);
取得对方主机的 SOCKET 信息
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
获得DNS信息:
struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const char *addr, int len, int type);
返回了一个指向 struct hostent 的指针,struct hostent 定义如下:
struct hostent {
char *h_name; /* 主机域名 */
char **h_aliases; /* 别名*/
int h_addrtype; /* 地址类型 */
int h_length; /* IP 地址长度 */
char **h_addr_list; /* IP 地址链*/
};
#define h_addr h_addr_list[0]
gethostbyname 通过设置 h_errno 代表出错号,对应的错误函数有hstrerror()和 herror(),分别对应于 strerror()和perror()这两个普通的错误函数。 获得或改变socket 属性
int getsockopt(int sockfd, int level, int name, char *value, int *optlen);
int setsockopt(int sockfd, int level, int name, char *value, int *optlen);
level:(级别): 指定选项代码的类型。
SOL_SOCKET: 基本套接口
IPPROTO_IP: IPv4 套接口
IPPROTO_IPV6: IPv6 套接口
IPPROTO_TCP: TCP 套接口
level 一般为常数SOL_SOCKET
name 选项名称
value 选项值:是一个指向变量的指针,变量可以是整形,套接口结构, 其他结构类型:linger{},timeval{}
optlen optval 的大小 常用选项的有:
[SOL_SOCKET
]SO_BROADCAST允许发送广播数据int
适用於 UDP socket 。其意义是允许 UDP socket「广播(broadcast)讯息到网路上。
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
检测对方主机是否崩溃,避免(服务器)永远阻塞于 TCP 连接的输入。 设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP 就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的 TCP 分节.它会导致以下三种情况: 对方接收一切正常:以期望的 ACK 响应。2 小时后,TCP 将发出另一个探测分节。 对方已崩溃且已重新启动:以 RST 响应。套接口的待处理错误被置为 ECONNRESET,套接 口本身则被关闭。对方无任何响应:源自 berkeley 的TCP 发送另外 8 个探测分节,相隔 75 秒一个,试图得到一个响应。在发出第一个探测分节 11 分钟 15 秒后若仍无响应就放弃。套接口的待处理错 误被置为 ETIMEOUT,套接口本身则被关闭。如 ICMP 错误是“host unreachable(主机不 可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH 。
SO_DONTLINGER 若为真,则 SO_LINGER 选项被禁止。
SO_LINGER 延迟关闭连接
struct linger上面这两个选项影响 close 行为选项 间隔 关闭方式 等待关闭与否
SO_DONTLINGER 不关心 优雅 否
SO_LINGER 零 强制 否
SO_LINGER 非零 优雅 是
若设置了 SO_LINGER(亦即linger 结构中的 l_onoff 域设为非零,参见 2.4,4.1.7 和 4.1.21
各节),并设置了零超时间隔,则 closesocket()不被阻塞立即执行,不论是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭,因为套接口的虚电路立即被复位,且丢失了未发送的数据。在远端的 recv()调用将以 WSAECONNRESET 出错。
若设置了 SO_LINGER 并确定了非零的超时间隔,则 closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“ 优雅的”关闭。请注意如果套接口置为非阻塞且SO_LINGER 设为非零超时,则 closesocket()调用将以 WSAEWOULDBLOCK 错误返回。
若在一个流类套接口上设置了 SO_DONTLINGER (也就是说将linger 结构的 l_onoff 域设为零;参见 2.4,4.1.7,4.1.21 节),则 closesocket()调用立即返回。但是,如果可能,排队的数据将在套接口关闭前发送。
SO_OOBINLINE 带外数据放入正常数据流,在普通数据流中接收带外数据 int
SO_RCVBUF 接收缓冲区大小 int
设置接收缓冲区的保留大小,与 SO_MAX_MSG_SIZE 或 TCP 滑动窗口无关,如果一般发送的包很大很频繁,那么使用这个选项
SO_SNDBUF 发送
缓冲区大小 int
设置发送
缓冲区的保留大小
与 SO_MAX_MSG_SIZE 或 TCP 滑动窗口无关,如果一般发送的包很大很频繁,那么使用这个选项
每个套接口都有一个发送缓冲区和一个接收缓冲区。 接收缓冲区被 TCP 和 UDP 用来将接收到
的数据一直保存到由应用进程来读。
TCP:TCP 通告另一端的窗口大小。 TCP 套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据。 这就是 TCP 的流量控制, 如果对方无视窗口大小而发出了超过宙口大小的数据,则接 收方 TCP 将丢弃它。
UDP :当接收到的数据报装不进套接口接收缓冲区时,此数据报就被丢弃。UDP 是没有 流量控制的;快的发送者可以很容易地就淹没慢的接收者,导致接收方的 UDP 丢弃数据报。 SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
每个套接口都有一个接收低潮限度和一个发送低潮限度。它们是函数 selectt 使用的, 接收低潮限度是让 select 返回“可读”而在套接口接收缓冲区中必须有的数据总量。 ——对于一个 TCP或 UDP 套接口,此值缺省为 1。发送低潮限度是让 select 返回“可写” 而在套接口发送缓冲区中必须有的可用空间。对于TCP 套接口,此值常缺省为 2048。 对于 UDP 使用低潮限度, 由于其发送缓冲区中可用空间的字节数是从不变化的,只要 UDP 套接口发送缓冲区大小大于套接口的低潮限度,这样的 UDP 套接口就总是可写的。 UDP 没有发送缓冲区,只有发送缓冲区的大小。
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_BROADCAST:获得或设置 socket 状况,使之可以广播发送数据报。(只能用于 UDP 方式)。
SO_REUSEADDR:设置该 socket绑定的端口可以被重用。
注意:在 Linux 系统中,如果一个 socket绑定了某个端口,该socket 正常关闭或程序退出后,
在一段时间内该端口依然保持被绑定的状态,其他程序(或者重新启动的原程序)无法绑定该端口。可以通过调用以下语句避免该问题:
opt = 1;
len = sizeof(opt);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len); SO_EXCLUSIVEADDRUSE
独占模式使用端口,就是不充许和其它程序使用 SO_REUSEADDR 共享的使用某一端口。
在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分,也就是说低级权限的用户是可以重绑定在高级权限如服务启动的端口上的,这是非常重大的一个安全隐患,如果不想让自己程序被监听,那么使用这个选 获得或改变socket 的I/O属性:
int ioctl(int sockfd,long cmd,unsigned long* argp);
cmd属性类型,argp属性的参数。
常用的有:
FIONREAD,返回 socket缓冲区中未读数据的字节数
FIONBIO,argp 为零时为阻塞模式,非零时为非阻塞模式
SIOCATMARK ,判断是否有未读的带外数据(仅用于TCP 协议),返回 true 或false
int fcntl(int fd, int cmd, long arg);
F_SETFL,arp为 O_NONBLOCK 时进入非阻塞模式,为0 时进入阻塞模式。
F_GETFL,获得属性。 调用 ioctl获取系统网络接口的信息:
struct ifconf{
int ifc_len; //为ifc_buf缓冲区的大小
union{
caddr_t ifcu_buf; //被定义为 ifc_buf , 用户定义传递到内核
struct ifreq *ifcu_req; //被定义为 ifc_req, 内核返回到用户
}ifc_ifcu;
};
sturct ifreq{
char ifr_name[IFNAMSIZ]; //网卡名字,如 hme0,IFNAMSIZ=16
union {
struct sockaddr ifru_addr; //定义为 ifr_addr,主 IP 地址;
struct sockaddr ifru_dstaddr; //定义为 ifr_dstaddr;
struct sockaddr ifru_broadaddr; //定义为 ifr_broadaddr;
short ifru_flags; //定义为 ifr_flags,参考<net/if.h >
int ifru_flags; //定义为 ifr_flags
int ifru_metric; //定义为 ifr_metric
caddr_t ifru_data; //定义为 ifr_data
}ifr_ifru;
}
例子:(在 SOLARIS 环境下通过)
输入 IP 地址,返回 MAC 地址:
#include <sys/types.h >
#include <sys/sockio.h >
#include <sys/param.h >
#include <errno.h >
#include <arpa/inet.h >
#include <sys/socket.h >
#include <netdb.h >
#include <sys/types.h >
#include <unistd.h >
#include <stropts.h >
#include <net/if.h >
#include <stdio.h >
#include <sys/ioctl.h >
#include <net/if_arp.h > int main(int argc, char **argv)
{
int sockfd;
char *ptr;
struct arpreq arp;
struct sockaddr_in *arptr; sockfd = socket(AF_INET, SOCK_STREAM, 0); //create any type of socket
arptr = (struct sockaddr_in *)&arp.arp_pa;
bzero(arptr, sizeof(struct sockaddr_in));
arptr- >sin_family = AF_INET;
arptr- >sin_addr.s_addr = inet_addr(argv[1]);
ioctl(sockfd, SIOCGARP, &arp);
printf("addr = %s, arp mac = %x %x %x %x %x %x %x %x\n",
inet_ntoa(arptr- >sin_addr), arp.arp_ha.sa_data[0], arp.arp_ha.sa_da
ta[1], arp.arp_ha.sa_data[2], arp.arp_ha.sa_data[3], arp.arp_ha.sa_data[4],
arp.arp_ha.sa_data[5], arp.arp_ha.sa_data[6], arp.arp_ha
.sa_data[7], arp.arp_ha.sa_data[8]);
ptr = arp.arp_ha.sa_data;
printf("mac = %x:%x:%x:%x:%x:%x\n", *(ptr)&0xff, *(ptr+1)&0xff,
*(ptr+2)&0xff, *(ptr+3)&0xff, *(ptr+4)&0xff, *(ptr+5)&0xff);
}
根据网卡名字输出 MAC地址:
#include <stdio.h >
#include <sys/types.h >
#include <sys/socket.h >
#include <sys/ioctl.h >
#include <netinet/in.h >
#include <net/if.h >
#include <net/if_arp.h >
#include <arpa/inet.h >
#include <errno.h >
#include <sys/sockio.h > #define ETH_NAME "eri0" int main()
{
int sock;
struct sockaddr_in sin;
struct ifreq ifr; sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock -1)
perror("socket");
return -1;
} strncpy(ifr.ifr_name, ETH_NAME, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ - 1] = 0; if (ioctl(sock, SIOCGIFADDR, &ifr) < 0)
{
perror("ioctl");
return -1;
} memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
fprintf(stdout, "hme0: %s\n", inet_ntoa(sin.sin_addr)); return 0;
}
输出当前系统所有 UP 状态的网络接口的 IP 地址,MAC 地址等信息
#include <sys/types.h >
#include <sys/sockio.h >
#include <sys/param.h >
#include <errno.h >
#include <arpa/inet.h >
#include <sys/socket.h >
#include <netdb.h >
#include <sys/types.h >
#include <unistd.h >
#include <stropts.h >
#include <net/if.h >
#include <stdio.h >
#include <sys/ioctl.h >
#include <net/if_arp.h >
#define INTERFACE_NUM 5
struct my_inte{
char inte_name[16];
char mac[8];
short mac_len;
short flags;
struct sockaddr primary_addr;
struct sockaddr broad_addr;
struct sockaddr dest_addr;
struct sockaddr netmask;
struct my_inet *next;
};
void list(struct my_inte* ptr)
{
struct sockaddr_in *addr;
printf("inteface_name = %s\n", ptr- >inte_name);
printf("mac = %x:%x:%x:%x:%x:%x\n", *(ptr- >mac)&0xff, * (ptr- >mac+1)&0xff,
*(ptr- >mac+2)&0xff, *(ptr- >mac+3)&0xff, *(ptr- >mac
+4)&0xff, *(ptr- >mac+5)&0xff);
printf("flags = %d ", ptr- >flags);
if (ptr- >flags & IFF_UP) printf("up ");
if (ptr- >flags & IFF_BROADCAST) printf("broadcast ")
if (ptr- >flags & IFF_MULTICAST) printf("multicast ");
if (ptr- >flags & IFF_LOOPBACK) printf("loopback ");
if (ptr- >flags & IFF_POINTOPOINT) printf("point to point ");
printf("\n");
addr = (struct sockaddr_in*)&ptr- >primary_addr;
printf("primary_addr = %s\n", inet_ntoa(addr- >sin_addr));
addr = (struct sockaddr_in*)&ptr- >netmask;
printf("netmask = %s\n", inet_ntoa(addr- >sin_addr));
addr = (struct sockaddr_in*)&ptr- >broad_addr;
printf("broad_addr = %s\n", inet_ntoa(addr- >sin_addr));
addr = (struct sockaddr_in*)&ptr- >dest_addr;
printf("dest_addr = %s\n\n", inet_ntoa(addr- >sin_addr));
}
int main(int argc, char **argv)
{
int sockfd, len, lastlen, flags;
char *buf, *ptr;
struct ifconf interfaces;
struct ifreq *inteptr, intecopy;
struct my_inte *head, *tail, *my_inte_ptr, *tmpptr;
struct arpreq arp;
struct sockaddr_in *arptr;
sockfd = socket(AF_INET, SOCK_STREAM, 0); //create any type of socket
lastlen = 0;
len = INTERFACE_NUM * sizeof(struct ifreq);
for (;;) {
buf = (char *)malloc(len);
interfaces.ifc_len = len;
interfaces.ifc_buf = buf;
if (ioctl(sockfd, SIOCGIFCONF, &interfaces) < 0){
if (errno != EINVAL || lastlen != 0){ //if return EINVAL and not
return succeed
perror("ioctl error");
return;
}
}
else
{
if (interfaces.ifc_len == lastlen)
break;
lastlen = interfaces.ifc_len;
}
len += INTERFACE_NUM * sizeof(struct ifreq);
free(buf);
}
head = tail = NULL;
for (ptr = buf; ptr < buf + interfaces.ifc_len; ){
inteptr = (struct ifreq *)ptr;
ptr += sizeof(inteptr- >ifr_name) + sizeof(struct sockaddr); //ptr pioint
to next ifreq struct
intecopy = *inteptr;
ioctl(sockfd, SIOCGIFFLAGS, &intecopy);
flags = intecopy.ifr_flags;
if (flags & IFF_UP == 0) //ignore down network card
continue;
my_inte_ptr = (struct my_inte_ptr*)calloc(1, sizeof(struct my_inte));
//create a self-defined struct
my_inte_ptr- >next = NULL;
my_inte_ptr- >flags = flags;
bzero(&(my_inte_ptr- >inte_name), 16);
strncpy(my_inte_ptr- >inte_name, inteptr- >ifr_name, 16-1);
memcpy(&my_inte_ptr- >primary_addr, &(*inteptr).ifr_addr , sizeof(struct
sockaddr)); //IP addr
arptr = (struct sockaddr_in *)&arp.arp_pa;
bzero(arptr, sizeof(struct sockaddr_in));
arptr- >sin_family = AF_INET;
arptr- >sin_addr = ((struct sockaddr_in
*)(&my_inte_ptr- >primary_addr))- >sin_addr;
ioctl(sockfd, SIOCGARP, &arp);
printf("addr = %s, arp mac = %x %x %x %x %x %x %x %x\n",
inet_ntoa(arptr- >sin_addr), arp.arp_ha.sa_data[0], arp.arp_ha.sa_da
ta[1], arp.arp_ha.sa_data[2], arp.arp_ha.sa_data[3], arp.arp_ha .sa_data[4],
arp.arp_ha.sa_data[5], arp.arp_ha.sa_data[6], arp.arp_ha
.sa_data[7], arp.arp_ha.sa_data[8]);
memcpy(my_inte_ptr- >mac, arp.arp_ha.sa_data, 8); //MAC
ioctl(sockfd, SIOCGIFNETMASK, &intecopy);
my_inte_ptr- >netmask = intecopy.ifr_addr;
if (flags & IFF_BROADCAST){
ioctl(sockfd, SIOCGIFBRDADDR, &intecopy);
my_inte_ptr- >broad_addr = intecopy.ifr_broadaddr;
}
if (flags & IFF_POINTOPOINT){
ioctl(sockfd, SIOCGIFDSTADDR, &intecopy);
my_inte_ptr- >dest_addr = intecopy.ifr_dstaddr;
}
if (head == NULL)
head = tail = my_inte_ptr;
else
tail- >next = my_inte_ptr;
}
free(buf);
//list the list
for (my_inte_ptr = head; my_inte_ptr != NULL; ){
tmpptr = my_inte_ptr;
my_inte_ptr= my_inte_ptr- >next;
list(tmpptr);
free(tmpptr);
}
}
ARP 高速缓存:
由 ioctl 维护
stuct arpreq{
struct sockaddr apr_pa;//IP 地址
struct sockaddr arp_ha;//MAC 地址
int arp_flags;
}
相关的操作有:
1. SIGCSARP:增加或修改一个 ARP 高速缓冲区的条目
2. SIGCDARP:删除一个条目
3. SIGCGARP:取出一个条目
路由套接口:
通过路由套接口交换的结构有 3 种:
1. Struct rt_msghdr{
U_short rtm_msgl en;//消息长度,包括头和其后的 SOCKET 地址结构
U_char rtm_version;
U_char rtm_type; //操作类型,如 RTM_GET
U_short rtm_index;
int rtm_flags;
int rtm_addrs; //位掩码,如 RTA_DST,表示头结构后的 SOCKET 地址结构的类型
pid_t rtm_pid;
int rtm_seq;
int rtm_errno;
int rtm_inits;
struct rt_metrics rtm_rmx;
}
2. Struct if_msghdr{…}
3. Struct ifa_msghdr{…}
以上消息结构由用户填写部分信息发往内核,返回的数据在 struct …_msghdr 结构之后将最多有 8 个 SOCKET 结构,为:DST、GATEWAY、NETMASK、GENMASK、IFP、IFR、AUTHOR、BRD 套接字结构
本章内容来自于互联网,小编只负责排版,因受小编技术所限,有时断句的地方并不准确,但小编已经尽力让本章整理的更有可读性,请谅解。因小编保存这篇文章时间过长,已记不清来源,所以未标注来源,敬请谅解!)