您的位置:首页 > 理论基础 > 计算机网络

WebBench源码剖析(中)

2017-05-24 22:30 671 查看
上一篇文章对webBench压力测试工具的基本功能、如何使用、大致框架给出了一些解释。如果解释的哪些地方不到位,欢迎请各位及时指正!

这一篇博文中,我们对webBench的源代码进行分析。整个webBench是用c语言写的,只有两个代码文件socket.c和webbench.c。首先我们来看下socket.c文件中的代码。

一、socket.c代码剖析

#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
//创建Socket连接,建立TCP(SOCK_STREAM)连接
int Socket(const char *host, int clientPort)
{
    int sock;
    unsigned long inaddr;
//------------------------------------------------------为了获取一个sockaddr_in结构体的对象,用于后边建立TCP连接
    struct hostent *hp;
struct sockaddr_in ad;//这个结构体用于建立TCP连接时使用,因为为了建立TCP连接,我们要获得这个结构体中的数据
    memset(&ad, 0, sizeof(ad));
    ad.sin_family = AF_INET;

    inaddr = inet_addr(host);//将点分十进制的字符串IP地址转化为网络字节序对应的数。这个函数与inet_aton一样都是将一个点分十进制的IP转换为网络字节序对应的数
    if (inaddr != INADDR_NONE)//转化是成功,说明host是一个点分十进制的IP
        memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
    else//host不是点分十进制的IP,而是一个主机名
    {
        hp = gethostbyname(host);//使用gethostbyname(const char *)获取该主机名对应的主机的信息,使用这个函数的目的主要是为了获取该主机名host对应的IP地址
        if (hp == NULL)//获取失败
            return -1;
        memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
    }
    ad.sin_port = htons(clientPort);//将client转换为网络字节序(转化为大端存储),一般在计算机中都是以小端存储的

//------------------------------------------------------应用程序调用socket函数来创建一个能够进行网络通信的套接字。
sock = socket(AF_INET, SOCK_STREAM, 0);//socket函数socket(int domain, int type, protocol);
    if (sock < 0)
        return sock;
    if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)//connect顾名思义就是拿来建立连接的函数
        return -1;//创建连接失败
    return sock;
}
文件中只包含了一个Socket函数,这个函数的功能是建立一个socket连接并且返回建立的socket描述符。

1.1 int Socket(char *host, int port);函数

形参:host:目的主机的域名或者点分十进制IP

 
   port:端口号

返回值:  如果函数执行成功,即成功连接到目的主机,返回建立的socket的描述符;

如果函数执行失败,返回-1

功能:根据一个目的主机名或者目的IP地址和端口号建立一个TCP连接。

1.2 Socket函数中的结构体和使用的函数

(1)hostent结构体

要包含的头文件:<netdb.h>和<sys/socket.h>

//hostent结构体
struct hostent
{
char *h_name; //主机名正规域名:如 www.google.com
char **h_aliases;  //主机别名:如google
int h_addrtype;  //IP地址的类型:IPV4(AF_INET)和IPV6(AF_INET6)
int h_length;  //IP地址的长度
char **h_addr_list;
#define h_addr h_addr_list[0]; //h_addr是指h_addr_list中的第一地址,是一个网络字节序的二进制IP地址。
};


hostent全名是“host entity”,顾名思义该结构体是用来描述“目的主机”的信息。通过一个gethostbyname函数可以获得该结构体的指针。

注意:h_addr是一个网络字节序(大端存储)的二进制IP地址,要使用函数inet_ntoa将其转化为主机字节序

(小端存储)。

(2)gethostbyname函数

所在头文件:<netdb.h>和<sys/socket.h>

函数原型:hostent* gethostbyname(char *name);

形参:name指主机域名或者主机名

返回值:成功,返回hostent结构体指针

失败,返回NULL

通常情况下根据已知主机名使用gethostbyname获取该主机的信息。

(3)sockaddr_in结构体

所在头文件:<netinet/in.h>

//sockaddr_in结构体
struct sockaddr_in
{
short sin_family;	/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;	/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;	/*IP地址的网络字节序(IP二进制位的大端存储)*/
unsigned char sin_zero[8];	/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
};


这个结构体是建立TCP连接的一个参数,所以整个Socket函数中就是为了填补sockaddr_in结构体中的成员。

一旦获取结构体中成员变量的值,我们就可以使用connect函数与目的主机建立连接,进行发送数据。

注意:该结构体中sin_port和sin_addr都是网络字节序,通常要使用函数htons()和inet_addr()做转化。

(4)unsigned int short htons(unsigned int short num);

所在头文件:<netinet/in.h>

形参:无符号短整型(16位)主机字节序数;

返回值:无符号短整型(16位)网络字节序数;

功能:将一个无符号短整型主机字节序数(小端存储)转化为无符号[b]短整型网络字节序([/b]大端存储)的数;

(5)unsigned
int short ntohs(unsigned int short num);

所在头文件:<netinet/in.h>

形参:无符号短整型(16位)网络字节序数;

返回值:无符号短整型(16位)主机字节序数;

功能:将一个无符号[b]短整型网络字节序[/b](小端存储)数转化为无符号[b]短整型主机字节序[/b](大端存储)的数;
(6)unsigned
int long htonl(unsigned int long num);

所在头文件:<netinet/in.h>

形参:无符号长整型(32位)主机字节序数;

返回值:无符号长整型(32位)网络字节序数;

功能:将一个无符号长整型主机字节序(小端存储)数转化为无符号长整型网络字节序(大端存储)的数;

(7)unsigned int long ntohl(unsigned
int long num);

所在头文件:<netinet/in.h>

形参:无符号长整型(32位)网络字节序数;

返回值:无符号长整型(32位)主机字节序数;

功能:将一个无符号长整型网络字节序(小端存储)数转化为无符号长整型主机字节序(大端存储)的数;

(8)char* inet_ntoa(struct in_add netAddr);

所在头文件:<arpa/inet.h>

形参:网络字节序的网络地址;

返回值:成功,点分十进制IP字符串;

失败,NULL;

功能:将一个网络IP地址转化为点分十进制IP;

(9)in_addr_t inet_addr(char *IP);

所在头文件:<arpa/inet.h>

形参:IP是一个点分十进制的字符串IP地址

返回值:成功,返回一个该IP对应的二进制网络字节序(大端存储)对应的无符号整形数;

失败,INADDR_NODE;

功能:将一个点分十进制的IP字符串转化为网络字节序二进制位对应的无符号整型。

(10)in_addr结构体

所在头文件:<arpa/inet.h>

struct in_addr {
in_addr_t s_addr;
};


(11)in_addr_t 结构体

所在头文件:<arpa/inet.h>

in_addr_t就是一个unsigned int,顺序为网络字节序。

(12)int socket(int domain, int type, int protrol)函数

所在头文件:<sys/types.h>和<sys/socket.h>

函数原型:int socket(int domain, int type, int protocol);

形参:int domain:是指使用的协议域,通常是使用是AF_INET,AF_INET6等;

int type:是指使用传输协议,一般有SOCK_STREAM(TCP),SOCK_DRAM(UDP);

int protocol:通常设为0,协议类型由type确定;

返回值:成功,返回标志该socket的标识符;

失败,返回-1;

参见:http://blog.csdn.net/bian_qing_quan11/article/details/71713647

(13)int connect(int sd, sockadd *add, size_t len);

所在头文件:<sys/socket.h>和<sys/types.h>

函数原型:int connect(int sockfd, sockaddr *sever_addr, int addrLength);

形参:int sockfd是一个套接字文件描述符,代表这已经打开的套接字;

sockaddr* sever_addr是一个服务器的IP和服务器上某个端口号;

int addrLength是server_addr的长度,一般使用sizeof('server_addr)就可以;

返回值:成功连接,返回0;

失败,返回其他;

功能:使用connect函数的程序主动发起TCP连接。在connect函数中进行TCP三次握手建立连接,将本地的sockfd与目的主机server_addr建立连接。

通常是在客户端调用connect,将客户端本地的sockfd与服务器端的某个服务(server_addr)建立TCP连接。

注意:connect函数成功执行相当于TCP通过三次握手建立连接。

下一篇博客我们将对webbench.c文件进行详细解析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息