现在的位置: 首页 > 综合 > 正文

socket常用结构体详解

2013年01月28日 ⁄ 综合 ⁄ 共 11964字 ⁄ 字号 评论关闭

①sockaddr的数据结构: 

struct sockaddr{
    unsigned short sa_family;//地址族,AF_xxx   AF_INET 不涉及转序的问题
    char sa_data[14];        //14字节的协议地址,网络字节序
};

此时:

struct in_addr{
	unsigned long s_addr;
}

sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET。
sa_data是14字节协议地址。
此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构,sockaddr_in(在netinet/in.h中定义)。

②sockaddr_in的数据结构:

struct sockaddr_in { 
    short int sin_family; //地址族
    unsigned short int sin_port; //端口号
    struct in_addr sin_addr; //Internet地址
    unsigned char sin_zero[8]; //与struct sockaddr 一样的长度,16个字节
}; 

此时:

struct in_addr{
	union{
	struct{u_char s_b1,s_b2,s_b3,s_b4;}S_un_b;
	struct{u_short s_w1,s_w2;}S_un_w;
	u_long S_addr;
	}S_un;
};

sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
s_addr按照网络字节顺序存储IP地址
sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向sockadd的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,在最后用进行类型转换就可以了bzero((char*)&mysock,sizeof(mysock));//初始化

其中,inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。

说明:这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到和struct sockaddr同样的长度,能用bzero()或memset()函数将其置为零。指向sockaddr_in的指针和指向sockaddr的指针能相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你能在函数调用的时候将一个指向  sockaddr_in的指针转换为指向sockaddr的指针;或相反。 它们的通常用法如下:

socket sockfd;
struct sockaddr_in my_addr;


if ((sockfd=socket(AF_INET,SOCK_STREAM,0)) == -1){
    perror("socket");
    exit(1);
}


my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(PORT);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&(my_addr.sin_zero,8);//zero the rest of the struct


if (bind(sockfd, (struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1){
    perror("bind");
    exit(1);
}

==============

htons

#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
htons的功能:将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)
参数u_short hostshort: 16位无符号整数
返回值:TCP / IP网络字节顺序.
htons 是把你机器上的整数转换成“网络字节序”, 网络字节序是 big-endian,也就是整数的高位字节存放在内存的低地址处。 而我们常用的 x86 CPU (intel, AMD) 电脑是 little-endian,也就是整数的低位字节放在内存的低字节处。举个例子吧。假定你的port是
0x1234,
在网络字节序里 这个port放到内存中就应该显示成
addr    addr+1
0x12    0x34
而在x86电脑上,0x1234放到内存中实际是:
addr    addr+1
0x34    0x12
htons 的用处就是把实际内存中的整数存放方式调整成“网络字节序”的方式。

================================

htonl()

简述:
将主机的无符号长整形数转换成网络字节顺序。
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
hostlong:主机字节顺序表达的32位数。
注释:
本函数将一个32位数从主机字节顺序转换成网络字节顺序。
返回值:
htonl()返回一个网络字节顺序的值。
参见:
htons(), ntohl(), ntohs().
在Linux系统下:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
有些系统包含的头文件是 <netinet/in.h> 而不是 <arpa/inet.h>.[1]
相关函数:
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
网际协议在处理这些多字节整数时,使用大端字节序。
在主机本身就使用大端字节序时,这些函数通常被定义为空宏。

==================================================

③hostent的数据结构:      

struct hostent{
	char *h_name;
	char **h_aliases;
    int h_addrtype;
    int h_length;
    char **h_addr_list;
};

详细资料:
      h_name -- 主机的正式名称;
      h_aliases -- 空字节-主机的别名;
      h_addrtype -- 主机ip地址类型;ipv4(AF_INET),ipv6(AF_INET6)
      h_length -- 主机ip地址的比特长度;
      h_addr_list -- 零字节-主机网络地址指针;网络字节序,所以要打印出的话要调用inet_ntop()
批注:
      使用这个结构体,首先要包含2个头文件: #include<netdb.h>,#include<sys/socket.h>
       gethostbyname()成功时返回一个指向结构体hostent指针,或者是个空(NULL)指针。
 例子:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>


int main(int argc, char *argv[]) {


struct hostent *h; 


if (argc != 2) { /*检查命令行*/
   fprintf(stderr,"usage: getip address "); 
   exit(1); 
}


if ((h = gethostbyname(argv[1])) == NULL) { /*取得地址信息*/
   herror("gethostbyname"); 
   exit(1);
} 


printf("Host Name  : %s ", h->h_name); 
printf("IP Address : %s ",inet_ntoa(*((struct in_addr *)h->h_addr))); 


return 0; 
}

注意:在使用gethostbyname()时,不能用perror()打印错误信息(因为errno没有使用),应该调用herror().
struct hostent *gethostbyname(const char*name);这个函数的传入值是域名或者主机名,例如:“www.google.com”等等。传出值是一个hostent的结构。如果函数调用失败,将返回NULL。
④用ioctl获得本地ip地址时要用到两个结构体ifconf和ifreq,它们对于大多数人来说都是比较陌生的,这里给大家一种比较简单的理解方法,当然只一种帮助理解的方法,在描述中可能会有一些地方与真实定义有所出入,仅供参考.
首先先认识一下ifconf和ifreq:
//ifconf通常是用来保存所有接口信息的

struct ifconf 
{ 
    int ifc_len;      /* size of buffer */
    union {            
       char *ifc_buf; /* buffer address */ 
       struct ifreq *ifc_req; /* array of structures */
    };  
};     
#define ifc_buf ifc_ifcu.ifcu_buf 
#define ifc_req ifc_ifcu.ifcu_req 

//ifreq用来保存某个接口的信息,这个结构体定义在/usr/include/net/if.h,用来配置IP地址,激活接口,配置MTU等接口

struct ifreq{
#define IFHWADDRLEN 6
#define IFNAMSIZ IF_NAMESIZE
    union{
        char ifrn_name[IFNAMSIZ]; 
} 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]; 
        char ifru_newname[IFNAMSIZ];
        __caddr_t ifru_data;
} ifr_ifru;
};
#define ifr_name ifr_ifrn.ifrn_name 
#define ifr_hwaddr ifr_ifru.ifru_hwaddr 
#define ifr_addr ifr_ifru.ifru_addr 
#define ifr_dstaddr ifr_ifru.ifru_dstaddr 
#define ifr_broadaddr ifr_ifru.ifru_broadaddr 
#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_ifindex ifr_ifru.ifru_ivalue 
#define ifr_bandwidth ifr_ifru.ifru_ivalue 
#define ifr_qlen ifr_ifru.ifru_ivalue 
#define ifr_newname ifr_ifru.ifru_newname 
#define _IOT_ifreq _IOT(_IOTS(char),IFNAMSIZ,_IOTS(char),16,0,0)
#define _IOT_ifreq_short _IOT(_IOTS(char),IFNAMSIZ,_IOTS(short),1,0,0)
#define _IOT_ifreq_int _IOT(_IOTS(char),IFNAMSIZ,_IOTS(int),1,0,0)

上边这两个结构看起来比较复杂,我们现在把它们简单化一些,比如说现在我们向实现获得本地IP的功能。
我们的做法是:
1. 先通过ioctl获得本地所有接口的信息,并保存在ifconf中
2. 再从ifconf中取出每一个ifreq中表示ip地址的信息
具体使用时我们可以认为ifconf就有两个成员:
ifc_len:表示用来存放所有接口信息的缓冲区长度
ifc_buf:表示存放接口信息的缓冲区
所以我们需要在程序开始时对ifconf的ifc_len和ifc_buf进行初始化,接下来使用ioctl获取所有接口信息,完成后ifc_len存放实际获得的接口信息总长度并且信息被存放在ifc_buf中。接下来我们只需要从一个一个的接口信息获取ip地址信息即可。

#include <string.h>
#include <net/if.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <netinet/in.h> 


int main()
{
int i=0;
int sockfd;
struct ifconf ifconf;
unsigned char buf[512];
struct ifreq *ifreq;


//初始化ifconf
ifconf.ifc_len = 512;
ifconf.ifc_buf = buf;


if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
{
perror("socket");
exit(1);
}


ioctl(sockfd, SIOCGIFCONF, &ifconf); //获取所有接口信息
//接下来一个一个的获取IP地址
ifreq = (struct ifreq*)buf;


for(i=(ifconf.ifc_len/sizeof(struct ifreq)); i>0; i--)
{
// if(ifreq->ifr_flags == AF_INET){ //for ipv4
printf("name = [%s]\n", ifreq->ifr_name);
printf("local addr = [%s]\n",
inet_ntoa(((struct sockaddr_in*)&(ifreq->ifr_addr))->sin_addr));
ifreq++;
// }
}


return 0;
}

实习测试代码:

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT 80
#define MAXLINE 1024
#define MAX2LINE 2048
#define MAXFILENAME 32
#define MAXHOST 32
#define MAXLEN 256

#define MAXMAX 1000000

#define WORKER_NUMS 50

//pthread_mutex_t mutex_send,mutex_receive,mutex;//互斥量

typedef struct fdata{
        int nIndex;
        char host[MAXHOST];
        char dir[MAXLEN];
        struct fdata *next;
}FNode;


//////////////////////////////////////
FNode *readcfg(char *filename) 
{
        FILE *fp;
        FNode *head = NULL, *p1 = NULL, *p2 = NULL;
        char buf[MAXLINE],*s = NULL, *t = NULL, *k = NULL;

        int i,nIndex = 0;

        if((fp = fopen(filename,"r")) == NULL)
        {
                printf("cannot open the file !\n");
                exit(1);
        }

        while( !feof( fp ) )
        {

                p1 = (FNode *)malloc(sizeof(FNode));
                fgets( buf, MAXLINE, fp );
                s = strchr( buf, '/' );

                if( s )
                {
                        ++ nIndex;
                        t = s + 1;
                        *s = '\0';
                        i = 0;
                        for( k = buf; *k != '\0'; k++)
                        {
                                if( *k == ' ' || *k == '\t' )
                                {
                                        continue;
                                }
                                p1->host[ i++ ] = *k;
                        }
                        p1->host[ i ] = '\0';

                        p1->dir[0] = '/';
                        if(*t == '?')
                        {
                                ++ t;
                        }
                        i = 1;
                        for( k = t; *k != '\n'; k++ )
                        {
                                if( *k == ' ' || *k == '\t')
                                {
                                        continue;
                                }
                                p1->dir[ i++ ] = *k;
                        }
                        p1->dir[ i ] = '\0';
                        p1->nIndex = nIndex;

                        if( NULL == head )
                        {
                                head = p1;
                        }
                        else
                        {
                                p2->next = p1;
                        }
                        p2 = p1;
                }
                p1->next = NULL;
        }
        fclose( fp );
        return head;
}


int cs_connect(const unsigned char *host, const unsigned int port)
{
        int iSock;//连接socket的ID
        struct hostent* iSite;//根据host获取的主机信息
        struct sockaddr_in sockAddr;//socket address

        iSite = gethostbyname(host);
        if(NULL == iSite)
        {
                return -3;//获取主机地址失败
        }

        iSock = socket(AF_INET, SOCK_STREAM, 0);
        if(iSock < 0)
        {
                return -2;//创建套接字失败
        }

        memset(&sockAddr, 0, sizeof(struct sockaddr_in));

        memcpy(&sockAddr.sin_addr, iSite->h_addr_list[0], sizeof(iSite->h_addr_list[0]));

        sockAddr.sin_family = AF_INET;

        sockAddr.sin_port   = htons(port);

        if(connect(iSock, (struct sockaddr *)&sockAddr, sizeof(struct sockaddr)) < 0)
        {
                perror("oops: client\n");
                return -1;//连接失败
        }
        return iSock;
}

////////////////////////////////////////////////////
void printEment(FNode* tParam)
{
        FNode *p = tParam;
        while(NULL != p)
        {
                printf("nIndex=%d,host=%s,dir=%s\n",p->nIndex,p->host,p->dir);
                p = p->next;
        }
}

///////////////////////////////////////////////////
int getHtmlByUrl(FNode * tParam)
{
        char ch;
        FILE * fp;

        int sockfd,len,ret,flag,j,i = 1;

        char ilen[16],ibuf[MAX2LINE],buf[MAXMAX];

        char httpString[MAXLEN],txtName[MAXLEN];


        FNode *p = tParam;

        while(NULL != p)
        {
                //sprintf(httpString,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: Close\r\n\r\n",p->dir,p->host);

                //printf("httpString = %s\n",httpString);

                sockfd = cs_connect(p->host, PORT);

                if(sockfd < 0)
                {      
                        close(sockfd);
                        ++ i;
                        p = p->next;
                        printf("connect 连接失败");
                        continue;

                }

                sprintf(txtName, "html/%d.html", p->nIndex);

                printf("txtName = %s\n",txtName);

                sprintf(httpString,"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: Close\r\n\r\n",p->dir,p->host);

                printf("httpString = %s\n",httpString);

                len = strlen(httpString);

                if( write(sockfd, httpString, len) != len)
                {
                        close(sockfd);
                        printf("%s 发送失败.\n",httpString);
                        ++ i;
                        p = p->next;
                        continue;
                }

                j = 0;

                memset(buf, 0, sizeof(buf));
                while((ret = read(sockfd, &ch,1)) != 0)
                {
                        if(-1 == ret)
                        {
                                printf("this is here?");
                                //continue;
                                break;
                        }

                        if(ch == '<')
                        {
                                break;
                        }
                        buf[j ++ ] = ch;
                }
                buf[j] = '\0';
                //printf("ibuf = %s\n",buf);
                if( strstr(buf,"404") || strstr(buf,"400") || strstr(buf,"500") )
                {       
                        close(sockfd);
                        ++ i;
                        p = p->next;
                        printf("%s 死链接\n",txtName);

                        continue;

                }

                if( NULL == (fp = fopen(txtName, "w")))
                {
                        fp = fopen(txtName, "w");
                }
                fprintf(fp,"%c",'<');
                char *t= NULL,*s = strstr(buf, "Content-Length:");
                memset(ilen, 0,sizeof(ilen));

                if(NULL != s)
                {
                        j = 0;
                        for(t = s + 16; *t != ' ' && *t != '\n'; ++ t)
                        {
                                ilen[j ++] = *t;
                        }
                        ilen[j] = '\0';
                        len  = atoi(ilen);
                }
                else
                {
                        len  = MAXMAX;
                }

                //printf("ilen = %s\n",ilen);

                memset(buf, 0, sizeof(buf));


                //printf("Here\n");

                //flag = 0;
                while( (len <= MAXMAX) && (ret = read(sockfd, buf, len)) != 0)
                {
                        if(-1 == ret)
                        {
                                break;
                        }
                        fprintf(fp,"%s",buf);
                        memset(buf, 0, sizeof(buf));

                }

                fclose(fp);
                close(sockfd);
                //printf("This is Last!");
                ++ i;
                p = p->next;
        }
        return 1;
}

void main_thread(void * param)
{
        FNode * tParam = (FNode *)param;
        //pthread_mutex_lock (&mutex_send);
        //printEment(tParam);
        //pthread_mutex_unlock (&mutex_send);
        int ret = getHtmlByUrl(tParam);

        if (1 == ret )
        {
                printf("Good\n");
        }
        else
        {
                printf("Fail\n");

        }
        //printf("nIndex = %d,host = %s,dir = %s\n",tfParam->nIndex,tfParam->host, tfParam->dir);

}


unsigned int getListLen(FNode *head)
{
        unsigned int len = 0;
        FNode *p = head;
        while(NULL != p)
        {
                ++ len;
                p = p->next;
        }
        return len;
}


int main()
{
        //============================
        struct timeval start,end;
        int use_time;
        gettimeofday(&start, NULL);
        //============================

        char * fileName = "11.txt";
        FNode *head = readcfg(fileName);

        //printEment(head);
        FNode *p = head;

        unsigned int len = getListLen(head);

        int average = len / WORKER_NUMS;

        printf("len = %d,average = %d\n",len,average);

        //printEment(p);
        FNode *t[WORKER_NUMS],*q = NULL;

        int i = 0;
        len = 0;
        t[0] = p;
        while(i < WORKER_NUMS - 1)
        {
                ++ len;
                q = p;
                p = p->next;

                if(len == average)
                {      
                        len  = 0;
                        ++ i;
                        t[i] = p;
                        q->next = NULL;
                }

        }
        //printf("len = %d\n",getListLen(t[9]));
        //printEment(t[9]);

        pthread_t tid[WORKER_NUMS];

        for(i = 0; i < WORKER_NUMS; ++ i)
        {
                pthread_create(&tid[i], NULL, (void *)main_thread, (void *)t[i]);
        }

        //void *ec;

        for(i = 0; i < WORKER_NUMS; ++ i)
        {
                pthread_join(tid[i], NULL);
                printf("thread_tid %d exit.\n", i);

                //printf("thread_tid %d exit code is:%d ", i, (*(int*)ec));
        }

        //========================================
        gettimeofday(&end, NULL);
        use_time = 1000000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);
        use_time /= 1000000;
        printf("this program running uses %d  seconds.\n", use_time);
        //========================================

        return 1;
}

/*知识积累*/

/*client who want to connect to the server by host and port*/
int getConnectServerSocketfd(const unsigned char *host, const unsigned int port)  
{  

	/*连接socket的ID*/	
	int sock_fd;

	/*根据host获取的主机信息 */
    struct hostent* host_info;
    
	/*server socket address*/
	struct sockaddr_in sock_addr; 
  
	/*//获取主机信息*/
    if (NULL == (host_info = gethostbyname(host)))  
    {  
		perror("Get host information: fail\n");  
        return -3;  
    }  

	/*创建套接字*/
    if (-1 == (sock_fd = socket(AF_INET, SOCK_STREAM, 0)))  
    { 
		perror("Create socket: fail\n");  
		return -2;
    }  

    memset(&sock_addr, 0, sizeof(struct sockaddr_in));   

	sock_addr.sin_family = AF_INET;   
    sock_addr.sin_port   = htons(port);
	//sock_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
    memcpy(&sock_addr.sin_addr, host_info->h_addr_list[0], sizeof(host_info->h_addr_list[0]));        

	/*连接服务器*/
    if (-1 == connect(sock_fd, (struct sockaddr *)&sock_addr, sizeof(struct sockaddr)))  
    {  
        perror("Connect: fail\n");  
        return -1;  
    }  

    return sock_fd;  
}  

抱歉!评论已关闭.