网络篇五--编程扩展
一、网络分析
1、网络分析测试工具
常用调试测试工具:
- 使用telnet测试TCP服务器端
- 使用lsof
- 使用tcpdump
- 使用netstat
- 使用sniffer
- 使用
wireshark
- Chariot
… - SmartBit—硬件
wireshark的使用推荐:
网络分析工具——WireShark的使用(超详细)
2、网络封包格式(了解)
(1)以太网头
(2)IP头
(3)TCP头
(4)UDP头
3、TCP三次握手过程
二、扩展的库函数
1、网络信息检索
网络信息检索函数:
- gethostname() 获得主机名
- getpeername() 获得与套接口相连的远程协议地址
- getsockname() 获得本地套接口协议地址
gethostbyname()
根据主机名取得主机信息,endhostent()
- gethostbyaddr() 根据主机地址取得主机信息
- getprotobyname() 根据协议名取得主机协议信息
- getprotobynumber() 根据协议号取得主机协议信息
- getservbyname() 根据服务名取得相关服务信息
- getservbyport() 根据端口号取得相关服务信息
#include <netdb.h>
#include <sys/socket.h> /* for AF_INET */
extern int h_errno;
struct hostent *gethostbyname(const char *name);
struct hostent
{
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */
成功
时返回结构体地址
,失败
时返回NULL
并设置错误号h_errno
h_errno
: 错误号,出错时用herror()
,hstrerror()
打印错误信息h_addr_list
: 主机多个网络地址列表h_addr
:列表第一个网络地址
使用示例:
struct hostent *hs;
if((hs = gethostbyname(argv[2])) == NULL)
{
herror("gethostbyname:");
exit(-1);
}
2、网络属性设置
getsockopt
和setsockopt
:
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)
level
指定控制套接字的层次,可以取三种值:SOL_SOCKET
:通用套接字
选项.IPPROTO_IP
:IP
选项.IPPROTO_TCP
:TCP
选项.
optname
指定控制的方式(选项的名称)optval
获得或者是设置套接字选项.根据选项名称的数据类型
进行转换optlen
:数据长度。
3、超时检查
在网络通信中,很多操作会使得进程阻塞
TCP套接字中的recv/accept/connect
UDP套接字中的recvfrom
超时检测的必要性
- 避免进程在没有数据时无限制地阻塞
- 当设定的时间到时,进程从原操作返回继续运行
方法一:
设置socket
的属性 SO_RCVTIMEO
参考代码如下:
struct timeval tv;
tv.tv_sec = 5; // 设置5秒时间
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,
&tv, sizeof(tv)); // 设置接收超时
方法二:
用select
检测socket
是否’ready
’
参考代码如下:
struct fd_set rdfs;
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() // 从socket读取数据
}
方法三:
设置定时器
(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);
三、广播和组播
1、广播
- 前面介绍的数据包发送方式
只有一个接受方
,称为单播
- 如果同时发给局域网中的所有主机,称为
广播
只有
用户数据报(使用UDP协议
)套接字才能广播- 广播地址
- 以
192.168.1.0 (255.255.255.0) 网段
为例,最大的主机地址192.168.1.255
代表该网段的广播地址
- 发到该地址的数据包被所有的主机接收
255.255.255.255
在所有网段中都代表广播地址
- 以
广播发送
1、创建用户数据报套接字
2、缺省创建的套接字不允许广播数据包,需要设置属性
3、setsockopt可以设置套接字属性
4、接收方地址指定为广播地址
5、指定端口信息
6、发送数据包
//广播发送需设定属性,不让2系统
int on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
广播接收
1、创建用户数据报套接字
2、绑定本机IP地址和端口,绑定的端口必须和发送方指定的端口相同
3、等待接收数据
2、组播
- 单播方式只能发给一个接收方。
- 广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
组播
(又称为多播)是一种折中的方式
。只有加入某个多播组的主机才能收到数据
。- 多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)
网络地址:
A类地址
第1字节为网络地址,其他3个字节为主机地址。第1字节的最高位固定为0
1.0.0.1 – 126.255.255.255B类地址
第1字节和第2字节是网络地址,其他2个字节是主机地址。第1字节的前两位固定为10
128.0.0.1 – 191.255.255.255C类地址
前3个字节是网络地址,最后1个字节是主机地址。第1字节的前3位固定为110
192.0.0.1 – 223.255.255.255D类地址(组播地址)
不分网络地址和主机地址,第1字节的前4位固定为1110
224.0.0.1 – 239.255.255.255
组播发送:
1、创建用户数据报套接字
2、接收方地址指定为组播地址
3、指定端口信息
4、发送数据包
组播接收:
1、创建用户数据报套接字
2、加入多播组(重要)
3、绑定本机IP地址和端口,绑定的端口必须和发送方指定的端口相同
4、等待接收数据
加入多播组
struct ip_mreq
{
struct in_addr imr_multiaddr; //组播地址
struct in_addr imr_interface; //本机地址
};
示例:
struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(“235.10.10.3”);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
四、UNIX域套接字
socket
同样可以用于本地通信- 创建套接字时使用本地协议
AF_UNIX(或AF_LOCAL)
。
socket(AF_LOCAL, SOCK_STREAM, 0)
socket(AF_LOCAL, SOCK_DGRAM, 0)
- 分为
流式套接字
和用户数据报套接字
- 和其他进程间通信方式相比使用方便、效率更高
- 常用于前后台进程通信
本地地址结构
struct sockaddr_un // <sys/un.h>
{
sa_family_t sun_family;
char sun_path[108]; // 套接字文件的路径
};
填充地址结构
struct sockaddr_un myaddr;
bzero(&myaddr, sizeof(myaddr));
myaddr.sun_family = AF_UNIX;
strcpy(myaddr.sun_path, “/tmp/mysocket”);
到这里就结束啦!