Linux网络编程实例解析
***************************************************************************************************************************
作者:EasyWave 时间:2013.01.19
类别:Linux 应用实例源码 声明:转载,请保留链接
注意:如有错误,欢迎指正。这些是我学习的日志文章......
***************************************************************************************************************************
这段时间,因为项目的需要,了解了一下USBtoNet的驱动,同时采用IOCTL来设置MAC的地址,检测网卡的连接状态等等,因此,就从网络上了解了一下关于网络编程方面的知识.一般来说:Linux网络程序与内核交互的方法是通过ioctl来实现的,ioctl与网络协议栈进行交互,可得到网络接口的信息,网卡设备的映射属性和配置网络接口.并且还能够查看,修改,删除ARP高速缓存的信息,所以,我们先来了解一下ioctl函数的具体实现.
函数形式:
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
|
类别 |
Request |
说明 |
数据类型 |
|
套 接 口 |
SIOCATMARK SIOCSPGRP SIOCGPGRP |
是否位于带外标记 设置套接口的进程ID或进程组ID 获取套接口的进程ID或进程组ID |
int int int |
|
文 件 |
FIONBIN FIOASYNC FIONREAD FIOSETOWN FIOGETOWN |
设置/清除非阻塞I/O标志 设置/清除信号驱动异步I/O标志 获取接收缓存区中的字节数 设置文件的进程ID或进程组ID 获取文件的进程ID或进程组ID |
int int int int int |
|
接 口 |
SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx |
获取所有接口的清单 设置接口地址 获取接口地址 设置接口标志 获取接口标志 设置点到点地址 获取点到点地址 获取广播地址 设置广播地址 获取子网掩码 设置子网掩码 获取接口的测度 设置接口的测度 获取接口MTU (还有很多取决于系统的实现) |
struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq |
|
ARP |
SIOCSARP SIOCGARP SIOCDARP |
创建/修改ARP表项 获取ARP表项 删除ARP表项 |
struct arpreq struct arpreq struct arpreq |
|
路 由 |
SIOCADDRT SIOCDELRT |
增加路径 删除路径 |
struct rtentry struct rtentry |
|
流 |
I_xxx |
相关数据结构:
1):网络接口请求结构ifreq
struct ifreq {
#define IFHWADDRLEN 6 //6个字节的硬件地址,即MAC
union { char ifrn_name[IFNAMESIZ]; //网络接口名称
}ifr_ifrn;
union { struct sockaddr ifru_addr; //本地IP地址
struct sockaddr ifru_dstaddr;//目标IP地址
struct sockaddr ifru_broadaddr;//广播IP地址
struct sockaddr ifru_netmask;//本地子网掩码地址
struct sockaddr ifru_hwaddr;//本地MAC地址
short ifru_flags;//网络接口标记
int ifru_ivalue;//不同的请求含义不同
struct ifmap ifru_map;//网卡地址映射
int ifru_mtu;//最大传输单元
char ifru_slave[IFNAMSIZ];//占位符
char ifru_newname[IFNAMSIZE];//新名称
void __user* ifru_data;//用户数据
struct if_settings ifru_settings;//设备协议设置
}ifr_ifru; }
#define ifr_name ifr_ifrn.ifrn_name;//接口名称
#define ifr_hwaddr ifr_ifru.ifru_hwaddr;//MAC
#define ifr_addr ifr_ifru.ifru_addr;//本地IP
#define ifr_dstaddr ifr_ifru.dstaddr;//目标IP
#define ifr_broadaddr ifr_ifru.broadaddr;//广播IP
#define ifr_netmask ifr_ifru.ifru_netmask;//子网掩码
#define ifr_flags ifr_ifru.ifru_flags;//标志
#define ifr_metric ifr_ifru.ifru_ivalue;//接口侧度
#define ifr_mtu ifr_ifru.ifru_mtu;//最大传输单元
#define ifr_map ifr_ifru.ifru_map;//设备地址映射
#define ifr_slave ifr_ifru.ifru_slave;//副设备
#define ifr_data ifr_ifru.ifru_data;//接口使用 #define ifr_ifrindex ifr_ifru.ifru_ivalue;//网络接口序号 #define ifr_qlen ifr_ifru.ifru_ivalue;//传输单元长度 #define ifr_newname ifr_ifru.ifru_newname;//新名称 #define ifr_seeting ifr_ifru.ifru_settings;//设备协议设置
2):网卡设备属性ifmap
struct ifmap { //网卡设备的映射属性
unsigned long mem_start;//开始地址
unsigned long mem_end;//结束地址
unsigned short base_addr;//基地址
unsigned char irq;//中断号
unsigned char dma;//DMA
unsigned char port;//端口
}
3):网络配置接口ifconf
struct ifconf { //网络配置结构体是一种缓冲区
int ifc_len;//缓冲区ifr_buf的大小
union {
char__user *ifcu_buf; //绘冲区指针
struct ifreq__user* ifcu_req;//指向ifreq指针
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf;//缓冲区地址
#define ifc_req ifc_ifcu.ifcu_req;//ifc_req地址
4):ARP高速缓存操作arpreq
struct arpreq{
struct sockaddr arp_pa;//协议地址
struct sockaddr arp_ha;//硬件地址
int arp_flags;//标记
struct sockaddr arp_netmask;//协议地址的子网掩码
char arp_dev[];//查询网络接口的名称
}
ARP高速缓存操作,包含IP地址和硬件地址的映射表, 操作ARP高速缓存的命令字 SIOCDARP,SIOCGARP,SIOCSARP分别是删除ARP高速缓存的一条记录,获得ARP高速缓存的一条记录和修改ARP高速缓存的一条记录
相关例子[以下为网络摘录:已经上机验证过]:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <net/if.h> int main(int argc, char*argv[]) {
int s,sv6;
int err;
s = socket(AF_INET, SOCK_DGRAM, );
if (s < ) {
perror("socket error");
return -;
} struct ifreq ifr;
ifr.ifr_ifindex = ; //获得第2个网络接口的名称
err = ioctl(s, SIOCGIFNAME, &ifr);
if (err) {
perror("index error");
} else {
printf("the %dst interface is:%s\n", ifr.ifr_ifindex, ifr.ifr_name);
} memcpy(ifr.ifr_name, "eth0", );
err = ioctl(s, SIOCGIFFLAGS, &ifr);
if (!err) {
printf("SIOCGIFFLAGS:%d\n", ifr.ifr_flags);
}
err = ioctl(s, SIOCGIFMTU, &ifr);
if (!err) {
printf("SIOCGIFMTU:%d\n", ifr.ifr_mtu);
} err = ioctl(s, SIOCGIFHWADDR, &ifr);
if (!err) {
unsigned char* hw = ifr.ifr_hwaddr.sa_data;
printf("SIOCGIFHWADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", hw[], hw[],
hw[], hw[], hw[], hw[]);
} err = ioctl(s, SIOCGIFMAP, &ifr);
if (!err) {
printf(
"SIOCGIFMAP,mem_start:%d,mem_end:%d,base_addr:%d,ifr_map:%d,dma:%d,port:%d\n",
ifr.ifr_map.mem_start, ifr.ifr_map.mem_end,
ifr.ifr_map.base_addr, ifr.ifr_map.irq, ifr.ifr_map.dma,
ifr.ifr_map.port);
} err = ioctl(s, SIOCGIFINDEX, &ifr);
if (!err) {
printf("SIOCGIFINDEX:%d\n", ifr.ifr_ifindex);
} err = ioctl(s, SIOCGIFTXQLEN, &ifr);
if (!err) {
printf("SIOCGIFTXQLEN:%d\n", ifr.ifr_qlen);
} struct sockaddr_in *sin = (struct sockaddr_in*) &ifr.ifr_addr; //保存的是二进制IP
char ip[]; //字符数组,存放字符串
memset(ip, , );
err = ioctl(s, SIOCGIFADDR, &ifr);
if (!err) {
inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, ); //转换的字符串保存到ip数组中,第二个参数是要转换的二进制IP指针,第三个参数是转换完成存放IP的缓冲区,最后一个参数是缓冲区的长度
printf("SIOCGIFADDR:%s\n", ip);
} err = ioctl(s, SIOCGIFDSTADDR, &ifr);
if (!err) {
inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, );
printf("SIOCGIFDSTADDR:%s\n", ip);
} err = ioctl(s, SIOCGIFNETMASK, &ifr);
if (!err) {
inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, );
printf("SIOCGIFNETMASK:%s\n", ip);
} memset(&ifr, , sizeof(ifr));
memcpy(ifr.ifr_name, "eth0", );
ioctl(s, SIOCGIFBRDADDR, &ifr);
struct sockaddr_in *broadcast = (struct sockaddr_in*) &ifr.ifr_broadaddr; inet_ntop(AF_INET, &broadcast->sin_addr.s_addr, ip, ); //inet_ntop将二进制IP转换成点分十进制的字符串
printf("BROADCAST IP:%s\n", ip);
close(s);
}
实例二:[修改代码后,已经上机验证过] MAC地址的设置和获取
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
typedef unsigned char u8 #define LOGD(...) do {printf(__VA_ARGS__); printf("\n");} while(0) int set_mac(u8* addr, int len);
int get_mac(u8* addr, int len); int main(int argc, char*argv[])
{
int ret = ;
u8 addr[] = {0x00, 0x00, 0x00, 0x61, 0x20, 0x58}; ret = set_mac(addr, );
if (ret < )
{
LOGD("set_mac() error");
return ;
}
LOGD("set_mac() done");
ret = get_mac(addr, );
if (ret < )
{
LOGD("get_mac() error");
return ;
}
LOGD("get_mac() done");
char buf[] = {};
snprintf(buf, , "%02x:%02x:%02x:%02x:%02x:%02x",
addr[], addr[], addr[], addr[], addr[], addr[]);
LOGD("%s", buf);
return ;
} int set_mac(u8* addr, int len)
{
int s;
int ret;
struct ifreq ifr;
if (len < )
{
LOGD("set_mac(), invalid length");
return -;
}
s = socket(PF_INET, SOCK_DGRAM, );
if (s < )
{
LOGD("socket() error: %s", strerror(errno));
return -;
}
strcpy(ifr.ifr_ifrn.ifrn_name, "usb0");
ifr.ifr_ifru.ifru_hwaddr.sa_family = ;
memcpy(ifr.ifr_ifru.ifru_hwaddr.sa_data, addr, );
ret = ioctl(s, SIOCSIFHWADDR, &ifr);
if (ret != )
{
LOGD("ioctl(SIOCSIFHWADDR) error: %d(%s)", errno, strerror(errno));
return -;
}
return ;
} int get_mac(u8* addr, int len)
{
int s;
int ret;
struct ifreq ifr; if (len < )
{
LOGD("get_mac(), invalid length");
return -;
}
s = socket(PF_INET, SOCK_DGRAM, );
if (s < )
{
LOGD("socket() error: %s", strerror(errno));
return -;
}
strcpy(ifr.ifr_ifrn.ifrn_name, "usb0");
ret = ioctl(s, SIOCGIFHWADDR, &ifr);
if (ret != )
{
LOGD("ioctl(SIOCSIFHWADDR) error: %s", strerror(errno));
return -;
}
memcpy(addr, ifr.ifr_ifru.ifru_hwaddr.sa_data, );
return ;
}
实例三:网卡连接状态[修改后,已经上机验证]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h> typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned char u8;
typedef unsigned long long u64 #include <linux/sockios.h>
#include <linux/ethtool.h> int get_netlink_status(const char *if_name); int main(int argc, char* argv[])
{
if(argc != )
{
fprintf(stderr, "usage: %s <ethname>.\n", argv[]);
return -;
}
if(getuid() != )
{
fprintf(stderr, "Netlink Status Check Need Root Power.\n");
return ;
} printf("Net link status: %s.\n", get_netlink_status(argv[])==?"up":"down");
return ;
}
// if_name like "ath0", "eth0". Notice: call this function
// return value:
// -1 -- error , details can check errno
// 1 -- interface link up
// 0 -- interface link down.
int get_netlink_status(const char *if_name)
{
int skfd;
struct ifreq ifr;
struct ethtool_value edata;
edata.cmd = ETHTOOL_GLINK;
edata.data = ;
memset(&ifr, , sizeof(ifr));
strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name) - );
ifr.ifr_data = (char *) &edata;
if (( skfd = socket( AF_INET, SOCK_DGRAM, )) == )
return -;
if(ioctl( skfd, SIOCETHTOOL, &ifr ) == -)
{
close(skfd);
return -;
}
close(skfd);
return edata.data;
}
Linux网络编程实例解析的更多相关文章
- Linux多线程编程实例解析
Linux系统下的多线程遵循POSIX线程接口,称为 pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.顺便说一下,Linux ...
- Linux 网络编程实例
/*socket->bind->listen->accept->recv/recvfrom->send/sendto->close 客户端:socket->c ...
- Linux网络编程——原始套接字实例:MAC 头部报文分析
通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...
- 很全的linux网络编程技巧
本文转载自:http://www.cnblogs.com/jfyl1573/p/6476607.html 1. LINUX网络编程基础知识 1 1.1. TCP/IP协议概述 1 1.2. OSI参考 ...
- Linux网络编程&内核学习
c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: ...
- linux网络编程_1
本文属于转载,稍有改动,以利于学习. (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个 ...
- Linux网络编程入门 (转载)
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- [转] - Linux网络编程 -- 网络知识介绍
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- 【转】Linux网络编程入门
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
随机推荐
- 收缩SQL数据库日志
各位同学,相信大家在使用SQL数据库时,常常会遇到日志文件比数据库文件还在大的情况.以下有一简单的办法,可以快速的删除日志档.使用其大小变为540K. 供各位参考. DUMP TRANSACTION ...
- Uva 1588 Kickdown
这道题思路并不难想,在做题过程中主要遇到的困难有: 因为没有仔细的考虑边界情况,没有分析全面,导致因=没有取到而得不出正确结果,浪费的大量时间. 今后在做这类题目时,一定要先进行一个比较全面的分析+模 ...
- PHP取当前页面完整URL地址
#测试网址: http://localhost/blog/testurl.php?id=5 //获取域名或主机地址 echo $_SERVER['HTTP_HOST']."<br> ...
- 生日蛋糕 (codevs 1710) 题解
[问题描述] 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1<=i<=M)层蛋糕是半径为Ri,高度为Hi的圆柱 ...
- 12.python中的列表
先看列表是如何创建的: a = ['scolia', 123] b = list('scolia',123) 同样有两种创建方式,但一般用第一种. 列表和元祖最大的不同就是列表是可以修改的. 老规矩, ...
- linux C 管道
单一进程使用管道基本上毫无意义.管道一般用来子进程和父进程之间的通信,或者兄弟进程间的通信. 创建管道的主要函数是pipe #include<unistd.h> ]) pipe函数创建一个 ...
- next permutaion算法
算法描述: Find largest index i such that array[i − 1] < array[i]. Find largest index j such that j ≥ ...
- Linux 配置 vimrc
由于熟悉了Windows下利用编译器进行编程,所以在刚刚接触Linux后的编程过程中会感觉其vim编译器的各种不方便编写程序,在逐渐的学习过程中了解到可以通过配置vimrc使得vim编译时类似于VS. ...
- SSH使用缩写
需要经常ssh到其它机器上,但如果每次都使用主机命名或ip地址,那挺难受的.这里有2个方法可以在ssh登陆时缩写(简写)主机名. 1. 在~/.ssh中添加config文件 [toughhou@hd1 ...
- myeclipse激活+Aptana安装配置
一.Myeclipse安装激活. 安装过程一路向下. 1.破解公钥,确保MyEclipse没有开启,否则失败! 用WinRAR或7-zip打开安装目录下Common\plugins\com.genui ...