Socket Address Structures

代码
struct in_addr {
  in_addr_t   s_addr;           
/* 32-bit IPv4 address */
                                
/* network byte ordered */
};

struct sockaddr_in {
  uint8_t         sin_len;      
/* length of structure (16) */
  sa_family_t     sin_family;   
/* AF_INET */
  in_port_t       sin_port;     
/* 16-bit TCP or UDP port number */
                                
/* network byte ordered */
  
struct in_addr  sin_addr;     /* 32-bit IPv4 address */
                                
/* network byte ordered */
  
char            sin_zero[8];  /* unused */
};

 

 

考虑到通用性,一般都用下面这个:

 

struct sockaddr {
   uint8_t      sa_len;
   sa_family_t  sa_family;    
/* address family: AF_xxx value */
   
char         sa_data[14];  /* protocol-specific address */
};

 

 

一般都需要强制转换过来,例如:

 

struct sockaddr_in  serv;      /* IPv4 socket address structure */
/* fill in serv{} */
bind(sockfd, (
struct sockaddr *&serv, sizeof(serv));

 

 

几个书上的socket函数:

 

int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);

 

 

代码
struct hostent *gethostbyname (const char *hostname);

//他的返回值hostent结构如下:
struct hostent {
   
char  *h_name;       /* official (canonical) name of host */
   
char **h_aliases;    /* pointer to array of pointers to alias names */
   
int    h_addrtype;   /* host address type: AF_INET */
   
int    h_length;     /* length of address: 4 */
   
char **h_addr_list;  /* ptr to array of ptrs with IPv4 addrs */
};

 

姊妹函数:

struct hostent *gethostbyaddr (const char *addr, socklen_t len, int family);

 

 

socket option 套接口选项:

 

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);

 

 

这两个函数一个是取值,一个是设置,具体用法如下:

代码
//让socket descriptor可重用:
  
/*************************************************************/
  
/* Allow socket descriptor to be reuseable                   */
  
/*************************************************************/

  rc 
= setsockopt(listen_sd, SOL_SOCKET,  SO_REUSEADDR,
                  (
char *)&on, sizeof(on));
  
if (rc < 0)
  {
    perror(
"setsockopt() failed");
    close(listen_sd);
    exit(
-1);
  }

 

 

 

 

在学习selectpoll函数之前,先来了解下五个I/O模型:

阻塞I/O

非阻塞I/O

I/O复用

信号驱动

异步I/O

 

 

socket默认是阻塞的,可以通过ioctl来将其设为非阻塞:

代码
/*************************************************************/
/* Set socket to be nonblocking. All of the sockets for    */
/* the incoming connections will also be nonblocking since  */
/* they will inherit that state from the listening socket.   */
/*************************************************************/
  rc 
= ioctl(listen_sd, FIONBIO, (char *)&on);
  
if (rc < 0)
  {
    perror(
"ioctl() failed");
    close(listen_sd);
    exit(
-1);
  }

 

 

当然也可以用fcntl

代码
int  flags;
if  ( (flags = fcntl (fd, F_GETFL, 0)) < 0//先取flag
    err_sys("F_GETFL error");
flags 
|= O_NONBLOCK;  //用或运算将NONBLOCK的值给添加进变量flags
if (fcntl(fd, F_SETFL, flags) < 0//设置flag
    err_sys("F_SETFL error");

 

 

select函数

select系统调用是用来让我们的程序监视多个文件描述符(file descrīptor)的状态变化的。程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改变。select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件描述符(不管是Socket描述符,还是其他文件或命名管道或设备描述符)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读,

 

select函数原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

 

函数的最后一个参数timeout显然是一个超时时间值,其类型是struct timeval *,即一个struct timeval结构的变量的指针,所以我们在程序里要申明一个struct timeval tv;然后把变量tv的地址&tv传递给select函数。struct timeval结构如下:

struct timeval {
long   tv_sec;     /* seconds */
long   tv_usec;    /* microseconds */
};

234三个参数的类型是一样的: fd_set *,即我们在程序里要申明几个fd_set类型的变量,比如定义了rfds, wfds, efds

另外关于fd_set类型的变量,还有一组标准的宏定义来处理此类变量:

FD_ZERO(fd_set *fdset):清空fdset与所有文件描述符的联系。

FD_SET(int fd, fd_set *fdset):建立文件描述符fdfdset的联系。

FD_CLR(int fd, fd_set *fdset):清除文件描述符fdfdset的联系。

FD_ISSET(int fd, fd_set *fdset):检查fd_set联系的文件描述符fd是否可读写,>0表示可读写。

(关于fd_set及相关宏的定义见/usr/include/sys/types.h)定义的这三个参数都是描述符的集合,第一个rfds是用来保存这样的描述符的:当描述符的状态变成可读的时系统就会告诉select函数返回,第二个wfds是指有描述符状态变成可写的时系统就会告诉select函数返回,第三个参数efds是特殊情况,即描述符上有特殊情况发生时系统会告诉select函数返回。下面以一个输入为例来说明:

 

代码
int fd1, fd2;         /* 在定义两个描述符*/
fd1 
= socket(...);    /* 创建socket连接*/
fd2 
= open(“/dev/tyS0”,O_RDWR); /* 打开一个串口*/
FD_ZERO(
&rfds);       /* 用select函数之前先把集合清零 */
FD_SET(fd1, 
&rfds);   /* 分别把2个描述符加入读监视集合里去 */
FD_SET(fd2, 
&rfds);
int maxfd = 0;
maxfd 
= (fd1>fd2)?(fd1+1):(fd2+1);           /* 注意是最大值还要加1 */
ret 
= select(maxfd, &rfds, NULL, NULL, &tv); /*然后调用select函数*/

//这样就可以使用一个开关语句(switch语句)来判断到底是哪一个输入源在输入数据。具体判断如下:
switch(ret){
   
case -1:perror("select");/* 这说明select函数出错 */
   
case 0:printf("超时\n"); /* 说明在设定的时间内,socket的状态没有发生变化 */
   
default
      
if(FD_ISSET(fd1, &rfds)) 处理函数1();/*socket有数据来*/
     
if(FD_ISSET(fd2, &rfds)) 处理函数2();/*ttyS0有数据来*/
}

 

 

 poll函数

 

#include <poll.h>
int poll (struct pollfd *fdarray, unsigned long nfds, int timeout);

 

 

nfdspollfd 数组的个数。

pollfd结构如下:

struct pollfd {
  
int     fd;       /* descriptor to check */
  
short   events;   /* events of interest on fd */
  
short   revents;  /* events that occurred on fd */
};