①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; }