网络编程基础
一、Socket概述
        Socket接口是TCP/IP网络的API,它定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应
    用程序。常见的Socket有以下三种类型。
        流式Socket(SOCK_STREAM): 提供可靠的、面向连接的通信流。使用TCP协议
        数据报Socket(SOCK_DGRAM): 定义了一种无连接的服务,使用UDP协议,不保证传输的可靠性和数据的原
                                  始顺序
        原始Socket:允许对底层协议进行直接访问,如IP,它的功能强大,用户可以通过该Socket开发自己的
                    协议
二、基本数据结构和函数
1、网络地址
(1)结构体sockaddr
    struct sockaddr{
        unsigned short sa_family;//地址族,AF_xxx
        char sa_data[14];//14字节的协议地址
    };
    
    sa_family: 一般为AF_INET,代表Internet(TCP/IP)地址族的IPV4协议
    sa_data: 包含了一些远程计算机的IP地址、端口号和套接字的数目,这些数据是混杂在一起的
    
(2)结构体sockaddr_in
    struct sockaddr_in{
        short int sin_family;        //地址族
        unsigned short int sin_port;//端口号
        struct in_addr sin_addr;    //IP号
        unsigned char sin_zero[8];    
    };
    
    sin_zero:用来将结构体struct sockaddr_in结构填充到与struct sockaddr同样的长度,
              可以用bzero()或memset()函数将其置为零
    指向sockaddr_in的指针与指向sockaddr的指针可以相互转换。

2、IP地址转换
        网络上的IP都是点分十进制的格式,如“192.168.0.1”,struct in_addr结构中用的是32位的IP,
    如IP(C0A80001)是“192.168.0.1”,俩地址的转换函数:
        int inet_aton(const char * cp, stuct in_addr * inp)//点分十进制转为32位的IP输入(inp)
            返回值:成功——非零值  失败——0
        char * inet_ntoa(struct in_addr in)//32位的IP转为点分十进制
            返回值:成功——返回指向存储着点分十进制的静态缓冲区的指针 失败——NULL

3、IP和域名的转换
    将域名转换为IP地址可以使用以下两个函数:
        struct hostent * gethostbyname(const char * hostname);
            功能:将机器名转换为一个结构指针,在这个结构里面储存了域名的信息
            返回值:失败时返回NULL,且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息
        struct hostent * gethostbyaddr(const char * addr, int len, int type);
            len: 字节长度(4)
            type: 族(AF_INET)
            功能:将一个32位的IP地址转换为结构指针
            返回值:失败时返回NULL,且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息
            
    hostent结构体:
        struct hostent{
            char * h_name;            //主机的正式名称
            char * h_aliases;        //主机的别名
            int h_addrtype;            //主机的地址类型AF_INET
            int h_length;             //主机的地址长度对于IP4是4字节32位
            char ** h_addr_list;     //主机的IP地址列表
        };
        #define h_addr h_addr_list[0]//主机的第一个IP地址

4、字节顺序转换
        计算机数据存储有两种字节优先顺序:高位字节优先和地位字节优先。优先顺序不同会出现数据不一致
    的情况,为了统一,linux中有专门的字节转换函数:
        unsigned long int htonl(unsigned long int hostlong);
        unsigned short int htons(unisgned short int hostshort);
        unsigned long int ntohl(unsigned long int netlong);
        unsigned short int ntohs(unsigned short int netshort);
        
        h: host
        n: network
        s: short
        l: long
        
5、服务信息函数(不常用)
        在网络程序中有时需要知道端口、IP和服务信息。可以使用以下函数获得:
        int getsockname(int sockfd, struct sockaddr * localaddr, int * addrlen);
        int getpeername(int sockfd, struct sockaddr * peeraddr, int * addrlen);
        struct servent * getservbyname(const char * servname, const char * protoname);
        struct servent * getservbyport(int port, const char * protoname);
        
        struct servent{
            char * s_name;        //正式服务名
            char ** s_aliases;    //别名列表
            int s_port;            //端口号
            char * s_proto;        //使用的协议
        };
        
三、Socket编程基础
    示例;(需要输入IP地址)

    int main(int argc, char ** argv){
        struct sockaddr_in addr;
        struct hostent * host;
        char ** alias;
        if(argc < 2){
            fprintf(stderr,"Usage:%s hostname|ip..\n\a",argv[0]);
            exit(1);
        }
        argv++;
        for(; *argv!=NULL; argv++){
            if(inet_aton(* argv, &addr.sin_addr)!=0){//将点分十进制转换为32位的IP存入addr.sin_addr
                host = gethostbyaddr((char *)&addr.sin_addr, 4, AF_INET);
                printf("Address information of IP %s\n", * argv);
            }
            else{
                host = gethostbyname(* argv);
                printf("Address information of IP %s\n", * argv);
            }
            
            if(host == NULL){
                fprintf(stderr, "No address information of %s\n", * argv);
                continue;
            }
            
            printf("Official host name %s\n",host->h_name);//显示主机名
            printf("Name aliases:");
            for(alias=host->h_aliases; * alias!=NULL;alias++)//显示所有别名
                printf("%s,",*alias);
            printf("\nIP address:");
            for(alias=host->h_addr_list; *alias!=NULL; alias++)//显示IP地址
                printf("%s,",inet_ntoa(*(struct in_addr *)(*alias)));//将32位的IP转换为点分十进制
        }
    }

    
三、TCP通信编程
1、创建套接字
        int socket(int family, int type, int protocol)
        
            family:协议或地址族
            type:套接字类型(SOCK_STREAM,SOCK_DGRAM)
            protocol: 协议号,0指定family和type的默认协议号
            返回值:成功——返回一个×××Socket描述符,错误——返回-1并设置error
            
2、绑定本地地址
    int bind(int sockfd, struct sockaddr * sa, int addrlen)
    
    sockfd: 套接字描述符
    sa: 指向struct sockaddr的指针,包含有关地址的信息(名称、端口和IP地址...)
    addrlen: 套接字地址接口的长度,可以设置为sizeof(struct sockaddr)
    返回值:成功——返回0, 失败——返回-1并设置error
    
    bind函数将Socket与本机上的一个端口相关联,随后即可在该端口监听服务请求。
    
3、listen()函数
    int listen(int sockfd, int backlog)
    
    backlog: 未经处理的连接请求队列可以容纳的最大数目
    
    listen()函数将套接字设置为监听模式,以等待连接请求。
    
4、accept()函数
    int accept(int sockfd, struct sockaddr * addr, int * addrlen)    
    
    addr: 一般是一个指向struct sockaddr_in结构的指针,将在accept函数调用返回后填入远程连接过来的
          计算机的信息,如远程计算机的IP地址和端口
    addrlen:表示参数addr所占的内存区大小,在accept函数调用返回后填入返回的addr结构体的大小
    
    accept()函数用于响应连接请求,建立连接并产生一个新的socket描述符来描述该连接,该连接用来与特定
    的客户端交换信息,默认为阻塞函数。
    
5、connect()函数
    int connect(int sockfd, struct sockaddr * serv_addr, int addrlen)
    
    serv_addr:存储远程计算机的IP地址和端口信息的sockadddr结构
    addrlen:是serv_addr结构体所占的内存大小
    返回值:成功——返回0,错误——返回-1
    
    connect()调用成功后,就可以使用sockfd作为服务器连接的套接字描述符,使用I/O函数进行数据传输了。
    
6、数据通信
(1)send()和recv()函数(最基本的、通过连接的套接字流进行通信的函数)

    int send(int sockdfd, const void * msg, int len, int flags);
        返回值:返回实际发送数据的长度,错误——返回-1        
    int recv(int sockfd, void * buf, int len, unsigned int flags);
        返回值:返回真正收到数据的长度,错误——返回-1
    
        sockfd: 与远程程序连接的套接字描述符
        msg: 指向想发送或存储信息的地址
        len: 信息的长度
        flags:发送或接收标记,一般设为0
    
(2)sendto()和recvfrom()函数(进行无连接的UDP通信时使用)

    int sendto(int sockfd, const void * msg, int len, unsigned int flags,
                const struct sockaddr * to, int tolen);                
    int recvfrom(int sockfd, void * buf, int len, unsigned int flags,
                    truct sockaddr * from, int fromlen);

(4)使用close()函数关闭当前的连接
    int close(int sockfd)