分类: C/C++
2013-05-18 23:23:11
套接字机制不但可以单机的不同进程通信,而且使得跨网机器间进程可以通信。
套接字的创建和使用与管道是有区别的,套接字明确地将客户端与服务器区分开来,套接字可以实现多个客户端连到同一服务器。
服务器套接字连接过程描述:
首先,服务器应用程序用socket创建一个套接字,它是系统分配服务器进程的类似文件描述符的资源。
接着,服务器调用bind给套接字命名。这个名字是一个标示符,它允许linux将进入的针对特定端口的连接转到正确的服务器进程。
然后,系统调用listen函数开始接听,等待客户端连接。listen创建一个队列并将其用于存放来自客户端的进入连接。
当客户端调用connect请求连接时,服务器调用accept接受客户端连接,accept此时会创建一个新套接字,用于与这个客户端进行通信。
客户端套接字连接过程描述:
客户端首先调用socket创建一个未命名套接字,让后将服务器的命名套接字作为地址来调用connect与服务器建立连接。
只要双方连接建立成功,我们就可以像操作底层文件一样来操作socket套接字实现通信。
几个基础函数定义:
#include
#include
int socket(it domain,int type,int protocal);
int bind(int socket,const struct sockaddr *address,size_t address_len);
int listen(int socket,int backlog);
int accept(int socket,struct sockaddr *address,size_t *address_len);
int connect(int socket,const struct sockaddr *addrsss,size_t address_len);
函数介绍如上过程描述。
struct sockaddr_un
{
sa_family_t sun_family; /*AF_UNIX*/
char sun_path[]; /*pathname*/
};
AF_INET域套接字地址格式:
struct sockaddr_in
{
short int sin_family; /*AF_INET:域*/
unsigned short int sin_port; /*Port number:端口*/
struct in_addr sin_addr; /*Internet address:地址*/
};
struct in_addr
{
unsigned long int s_addr;
};
通用地址类型格式:
struct sockaddr
{
unsigned short sa_family; /*address family,AF_XXX*/
char sa_data[14]; /*14 bytes of protocal address*/
};
sa_family是地址族,一般是"AF_XXX"的形式,常用AF_INET。
sa_data是14字节协议地址。
主机信息结构体:
struct hostent
{
char *h_name; /*name of the host:主机名*/
char **h_aliases; /*list of aliases(nicknames):主机别名数组*/
int h_addrtype; /*address type:地址类型如AF_INET*/
int h_length; /*length in bytes of the address:地址长度*/
char **h_addr_list; /*list of address(network order):主机所有地址*/
};
服务信息结构体:
struct servent
{
char *s_name; /*name of the server:服务名*/
char **s_aliases; /*list of aliases(alternative names):服务名队列*/
int s_port; /*The IP port number:连接该服务时用的端口号*/
char *s_proto; /*The server type,usually "tcp" or "udp":连接该服务用的协议名*/
};
#include
/*根据ip地址得到主机信息结构体*/
struct hostent *gethostbyaddr(const void *addr,size_t len,int type);
/*根据主机名得到主机信息结构体*/
struct hostent *gethostbyname(const char *name);
/*根据服务名,返回与给定服务名对应的包含名字和服务号信息的servent结构指针*/
struct servent *getservbyname(const char *name,const char *proto);
/*根据服务端口,返回与给定服务名对应的包含名字和服务号信息的servent结构指针*/
struct servent *getservbyport(int port,const char *proto);
计算机数据存储分高字节序和低字节序。Internet上是高字节序传输,如果我们的机器是低字节序则需要进行字节序转换。
高字节序(大端模式):低字节存储在低地址
低字节序(小端模式):高字节存储在低地址
提供字节序转换的函数:
#include
htonl():把unsigned long位值从主机字节序转换成网络字节序
htons():把unsigned short位值从主机字节序转换成网络字节序
ntohl():把unsigned long位值从网络字节序转换成主机字节序
ntohs():把unsigned short位值从网络字节序转换成主机字节序
IP地址转换常用函数
#include
/*网络字节序地址转换成字符串*/
char *inet_ntoa(struct in_addr in);
/*字符串转换成网络字节序地址*/
in_addr_t inet_addr(const char *cp);
socket的ioctl操作:
#include
ioctl(int fd,int request,void *arg);
功能 : 控制I/O设备, 提供了一种获得设备信息和向设备发送控制参数的手段.
参数 : int fd 文件句柄. 用于socket时, 是socket套接字.
int request 函数定义的所有操作. 关于socket的操作, 定义在
void *arg 指针的类型依赖于request参数.
类别 |
Request |
说明 |
数据类型 |
套 |
SIOCATMARK |
是否位于带外标记 |
int |
文 |
FIONBIN |
设置/ 清除非阻塞I/O 标志 |
int |
接 |
SIOCGIFCONF |
获取所有接口的清单 |
struct ifconf |
ARP |
SIOCSARP |
创建/ 修改ARP 表项 |
struct arpreq |
路 |
SIOCADDRT |
增加路径 |
struct rtentry |
流 |
I_xxx |
|
|
socket最常用到的结构体: struct ifreq 定义在
/* Structure used in SIOCGIFCONF request. Used to retrieve interface
configuration for machine (useful for programs which must know all
networks accessible). */
struct ifconf
{
int ifc_len; /* Size of buffer. */
union
{
__caddr_t ifcu_buf;
struct ifreq *ifcu_req;
} ifc_ifcu;
};
/* Interface request structure used for socket ioctl's. All interface
ioctl's must have parameter definitions which begin with ifr_name.
The remainder may be interface specific. */
struct ifreq
{
# define IFHWADDRLEN 6
# define IFNAMSIZ IF_NAMESIZE
union
{
char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
} ifr_ifrn;
union
{
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short int ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
__caddr_t ifru_data;
} ifr_ifru;
};
/* ARP ioctl request. */
struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
char arp_dev[16];
};
/* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
struct rtentry
{
unsigned long int rt_pad1;
struct sockaddr rt_dst; /* Target address. */
struct sockaddr rt_gateway; /* Gateway addr (RTF_GATEWAY). */
struct sockaddr rt_genmask; /* Target network mask (IP). */
unsigned short int rt_flags;
short int rt_pad2;
unsigned long int rt_pad3;
unsigned char rt_tos;
unsigned char rt_class;
#if __WORDSIZE == 64
short int rt_pad4[3];
#else
short int rt_pad4;
#endif
short int rt_metric; /* +1 for binary compatibility! */
char *rt_dev; /* Forcing the device at add. */
unsigned long int rt_mtu; /* Per route MTU/Window. */
unsigned long int rt_window; /* Window clamping. */
unsigned short int rt_irtt; /* Initial RTT. */
};
一、获取
以下例程通过ioctl获取设备"eth0"的IP/掩码/硬件址
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "net/if.h"
#include "arpa/inet.h"
#include "linux/sockios.h"
int main(int argc,char *argv[])
{
struct sockaddr_in *addr;
struct ifreq ifr;
char*address;
int sockfd;
char *name = "eth0";
if( strlen(name) >= IFNAMSIZ)
printf("device name is error.\n"), exit(0);
strcpy( ifr.ifr_name, name);
sockfd = socket(AF_INET,SOCK_DGRAM,0);
//get inet addr
if( ioctl( sockfd, SIOCGIFADDR, &ifr) == -1)
printf("ioctl error.\n"), exit(0);
addr = (struct sockaddr_in *)&(ifr.ifr_addr);
address = inet_ntoa(addr->sin_addr);
printf("inet addr: %s\n",address);
//get Mask
if( ioctl( sockfd, SIOCGIFNETMASK, &ifr) == -1)
printf("ioctl error.\n"), exit(0);
addr = (struct sockaddr_in *)&ifr.ifr_addr;
address = inet_ntoa(addr->sin_addr);
printf("Mask: %s\n",address);
//get HWaddr
u_int8_t hd[6];
if(ioctl(sockfd, SIOCGIFHWADDR, &ifr) == -1)
printf("hwaddr error.\n"), exit(0);
memcpy( hd, ifr.ifr_hwaddr.sa_data, sizeof(hd));
printf("HWaddr: %02X:%02X:%02X:%02X:%02X:%02X\n", hd[0], hd[1], hd[2], hd[3], hd[4], hd[5]);
exit(0);
}
二、设置
以下例程设置eth0的IP地址.
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "net/if.h"
#include "arpa/inet.h"
#include "linux/sockios.h"
int main(int argc,char *argv[])
{
char *dev = "eth0";
char *ip = "192.168.1.252";
struct ifreq ifr;
if( strlen(dev) >= IFNAMSIZ)
printf("device name error.\n"), exit(0);
else
strcpy( ifr.ifr_name, dev);
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
//get inet addr
if( ioctl( sockfd, SIOCGIFADDR, &ifr) == -1)
printf("ioctl error.\n"), exit(0);
struct sockaddr_in *addr = (struct sockaddr_in *)&(ifr.ifr_addr);
char * address = inet_ntoa(addr->sin_addr);
printf("current inet addr: %s\n",address);
//set inet addr
struct sockaddr_in *p = (struct sockaddr_in *)&(ifr.ifr_addr);
p->sin_family = AF_INET;
inet_aton( ip, &(p->sin_addr));
if( ioctl( sockfd, SIOCSIFADDR, &ifr) == -1)
printf("ioctl error.\n"), exit(0);
else
printf("change inet addr to: %s\n", ip);
//any OS need active dev.
/*ifr.ifr_flags |= IFF_UP;
if( ioctl( sockfd, SIOCSIFFLAGS, &ifr) == -1)
printf("active fault.\n"), exit(0);
else
printf("%s[%s] is working...\n", dev, ip);
*/
close(sockfd);
exit(1);
//end
}
屏蔽的代码用于设置IP后, 激活新设置. 多数系统不需要这步操作.
而且这步仅作演示. 真实使用的时候, 至少应该
1. 获取当前ifr.ifr_flags
2. ifr.ifr_flags |= IFF_UP;
/* ARP ioctl request. */
struct arpreq {
struct sockaddr arp_pa; /* protocol address */
struct sockaddr arp_ha; /* hardware address */
int arp_flags; /* flags */
struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
char arp_dev[16];
};