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