UNIX_C 高级编程<六>

来源:互联网 发布:淘宝行架 编辑:程序博客网 时间:2024/05/29 21:33

网络编程

    

     七层网络模型  `                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

 

         为了实现数据在网络中的传递,ISO将网络协议从逻辑上划分一下七层

             

         应用层 — 主要用于将数据交给应用程序,如:QQ等

         表示层 — 主要用于按照统一的格式进程数据的封装等

         会话层 — 主要用于控制对话的开始和结束等

         传输层 — 主要用于进行数据的错误检查和重新排序等

         网络层 — 主要用于选择具体的网络协议,再次封装和传输

     数据链路层 — 主要用于将数据转换为高低电平号等

         物理层 — 主要指交换机等物理设备等                                                                                                                                                     

 

     常用的网络协议

                                             

         TCP协议 —— 传输控制协议,是一种面向链接的协议,类似打电话

        UDP协议 —— 用户数据报协议,是一种非面向链接协议,类似写信

        IP协议  —— 互联网协议,是上述两种协议的底层协议

 

     IP地址和子网掩码(重点)

 

         IP地址 —— 本质就是互联网中的唯一地址标识

         IP地址本质上就是由32位二进制组成的整数(ipv4),由128位二进制组成的整数(ipv6)

 

         日常生活中采用点分十进制表示法来描述IP地址,即将没8位二进制转换成一个十进制的

         +整数,不同的十进制整数之间使用小数点分隔;

 

         为了便于IP地址的管理,将IP地址分为两部分:网络地址+主机地址,根据网络地址和主机

         +地址的不同分为一下4类IP地址:

             

         A类:0 + 7位网络地址+24为主机地址

         B类:10+14位网络地址+16位主机地址

         C类:110+21位网络地址+8位主机地址

         D类:1110+28位多播地址

 

     查看IP地址:

         windows系统ipconfig/all

         linux系统ifconfig

        

     子网掩码

        

         子网验码 —用于划分IP地址中网络地址和主机地址,具体划分方法为:IP地址&子网验码

 

               IP地址:172.   30 .4  .130

            子网验码:255.255.255.0      &

            -------------------------------

            网络地址:172.30 .4  .0      

 

     端口号和字节序

 

         IP地址 — 定位到具体某一台设备

         端口号 — 定位到设备中的具体某一个进程

         端口好本质就是unsigned short 类型,范围是0~65535,其中0~1024已经被系统占用,因此建议从1025开始使用

 

     字节序

         小端系统:低位字节的数据存放在低位内存地址

        大端系统:低位字节的数据存放在高位内存地址

 

     一般性原则:

         一般来说,将所有发送到网络中的多字节整数先转换成为网络字节序在发送,而将所有从网络中接收到的多字节整数先转换为主机字节序再解析,而网络字节序本上就是大端系统的字节序

 

基于socket的一对一通信模型

     socket - 逻辑通信载体;

    

     通信模型

    

     主机A:

        1.创建socket,使用socket函数;

        2.准备通信地址,使用结构体类型;

        3.绑定socket和通信地址,使用bind函数;

        4.进行通信,使用read/write函数;

        5.关闭socket,使用close函数

   

    主机B:

        1.创建socket,使用socket函数;

        2.准备通信地址,使用主机A的通信地址;

        3.链接socket和通信地址,使用connect函数;

        4.进行通信,使用read/write函数;

        5.关闭socket,使用close函数

 

     #include <sys/types.h>       

    #include <sys/socket.h>

    #include <sys/un.h>

 

 1、int sockfd = socket(int domain,int type, int protocol);

    

         参 1:域/地址族,决定是本地通信还是网络通信

                AF_UNIX/AF_LOCAL - 实现本地通信(在同一个主机)

                AF_INET - 实现基于ipv4的网络通信(不同主机)

                AF_INET6

         参 2:通信类型,决定了通信的方式

                   SOCK_STREAM - 表示提供可靠的,有序,双向的,面向链接的字节流通信方式也就是基于tcp协议的通信

                   SOCK_DGRAM - 表示提供不可靠的,非面向链接的数据包通信的方式,也就是基于udp协议

         参 3:用于指定特殊协议,默认给0即可

         返回:成功返回socket的描述符,失败返回-1

         功能:主要用于创建用于交流的通信点;

 

 2、通信地址的数据类型

         a.通用的通信地址类型

              struct sockaddr {

               sa_family_t sa_family;

               char        sa_data[14];

           } —— 该结构体主要用于函数的形参类型,很少定义变量

 

         b.基于本地通信的数据类型

              #include<sys/un.h>

              struct sockaddr_un{

               sa_family_t  sun_family//地址族 AF_UNIX

             char         sun_path[]//socket文件的路径名strcpy(addr.sun_path,"a.sock");

              };

         c.基于网络通信的数据类型

               #include <netinet/in.h>

              the sockaddr_in{

                   sa_family_t     sin_family   //协议族

                   in_port_t      sin_port     //端口

                   struct in_addr sin_addr     //ip地址

              };

 

              struct in_addr{

                    in_addr_t s_addr //整数类型的IP地址

              };

        

 3、int bind(int sockfd, const structsockaddr *addr, socklen_t addrlen);

         参 1:socket的描述符,socket函数的返回值

         参 2:结构体指针,可能传入结构体体变量的地址,可能需要做类型转换

         参 3:通信地址的大小,使用sizeof计算即可

         功能:主要用于绑定socket和通信地址;

 

 4、int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

        参 1:socket的描述符,socket函数的返回值

         参 2:结构体指针,可能传入结构体体变量的地址,可能需要做类型转换

         参 3:通信地址的大小,使用sizeof计算即可

 

         功能:主要用于链接socket和通信地址,参数和返回值与bind相同

 

 

      1 //实现基于socket的本地通信

 2  3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <sys/types.h> 8 #include <sys/socket.h> 9 #include <sys/un.h>10 11 int main(void)12 {13 14     //1.创建socket,使用socket函数15     int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);16     if( -1 == sockfd )17     {18         perror("socket");19         exit(-1);20     }21     printf("创建socket成功\n");22     //2.准备通信地址,使用结构体23         struct sockaddr_un addr;24         addr.sun_family= AF_UNIX;25         strcpy( addr.sun_path,"a.sock");26     //3.绑定socket和通信地址,使用bind函数27 28     int  res = bind(sockfd,(const struct sockaddr *) (&addr), sizeof(addr));29     if( -1 == res )30     {31         perror("bind");32         exit(-1);33     }34     printf("绑定socket和通信地址成功\n");35     //4.进行通信,使用read/write函数36     char buf[100] = {0};37 38     res = read(sockfd, buf, sizeof(buf));39     if(-1 == res )40     {41         perror("read");42         exit(-1);43     }44     printf("读取到的数据是:%s, 数据大小是:%d\n", buf, res);45 46     //5.关闭socket.使用close47 48     res = close(sockfd);49     if(-1 == res )50     {51         perror("close");52         exit(-1);53     }54 55     printf("成功关闭socket\n");56    

基于socket的一对一通信

 

     字节序转换的相关函数

 

         #include <arpa/inet.h>

 

         uint32_t htonl(uint32_t hostlong);   —— 32位主机字节序到网络字节序转换

 

         uint16_t htons(uint16_thostshort); —— 16位主机字节序到网络字节序转换

 

         uint32_t ntohl(uint32_t netlong);    —— 32为网络字节序到主机字节序转换

 

         uint16_t ntohs(uint16_t netshort);   —— 16位网络字节序到主机字节序转换

 

     IP地址的转换函数

 

         in_addr_t inet_addr(const char *cp); —— 主要用于将字符串形式的IP地址转换成为整数类型

 

         char *inet_ntoa(struct in_addr in);  —— 主要用于将结构体类型的IP地址转换成字符串类型;


//实现基于socket网络通信 2 //bind读取端的实现 3  4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <unistd.h> 7 #include <sys/types.h> 8 #include <sys/socket.h> 9 #include <netinet/in.h>10 #include <arpa/inet.h>11 12 int main(void)13 {14 15     //1.创建socket,使用socket函数16 17     int sockfd = socket(AF_INET, SOCK_DGRAM, 0);18     if( -1 == sockfd)19     {20         perror("socket error");21         exit(-1);22     }23 24     printf("创建socket成功\n");25 26     //2.准备通信地址,使用结构体类型27 28     struct sockaddr_in addr;29 30     addr.sin_family = AF_INET;31     addr.sin_port = htons(8888);32     addr.sin_addr.s_addr =inet_addr( "172.30.4.127" );33 34     //3.绑定socket和通信地址,使用bind函数35 36     int res = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr) );37     if( -1 == res )38     {39         perror("bind error");40         exit(-1);41     }42 43     printf("绑定socket和通信地址成功\n");44 45     char buf[100] = {0};46 47     //4.进行通信,使用read函数48 49     res = read(sockfd, buf, sizeof(buf));50     if(-1 == res )51     {52         perror("read ");53         exit(-1);54     }55 56     printf("成功读取数据的内容是:%s, 数据大小为:%d\n", buf, res);57     //5.关闭socket。使用close函数58 59     res = close(sockfd);60     if( -1 == res )61     {62         perror("close");63         exit(-1);64     }65 66     printf("成功关闭socket\n");67 68     return 0;69 }

通信模型

     服务器:

         1、创建socket,使用socket函数

         2、准备通信地址,使用结构体类型

         3、绑定socket和通信地址,使用bind函数

         4、监听,使用listen函数

         5、响应客户端的连接请求,使用accept函数

         6、进行通信,使用recv/send函数

         7、关闭socket, 使用close函数

 

     客户端

         1、创建socket,使用socket函数

         2、准备通信地址,使用服务器的地址

         3、绑定socket和通信地址,使用connect函数

         4、进行通信,使用recv/send函数

         5、关闭socket, 使用close函数

 

     相关函数的解析

 

     int sockfd = socket(int domain, int type,int protocol);

    

         参 1:域/地址族,决定是本地通信还是网络通信

                AF_UNIX/AF_LOCAL- 实现本地通信(在同一个主机)

                AF_INET - 实现基于ipv4的网络通信(不同主机)

                AF_INET6

         参 2:通信类型,决定了通信的方式

                   SOCK_STREAM - 表示提供可靠的,有序,双向的,面向链接的字节流通信方式也就是基于tcp协议的通信

                   SOCK_DGRAM - 表示提供不可靠的,非面向链接的数据包通信的方式,也就是基于udp协议

         参 3:用于指定特殊协议,默认给0即可

         返回:成功返回socket的描述符,失败返回-1

         功能:主要用于创建用于交流的通信点;

 

    通信地址的数据类型

         a.通用的通信地址类型

              struct sockaddr {

               sa_family_t sa_family;

               char        sa_data[14];

           } —— 该结构体主要用于函数的形参类型,很少定义变量

 

         b.基于本地通信的数据类型

              #include<sys/un.h>

              struct sockaddr_un{

                sa_family_t  sun_family //地址族AF_UNIX

               char        sun_path[] //socket文件的路径名strcpy(addr.sun_path,"a.sock");

              };

         c.基于网络通信的数据类型

              #include<netinet/in.h>

              the sockaddr_in{

                   sa_family_t     sin_family   //协议族

                   in_port_t      sin_port     //端口

                   struct in_addr sin_addr     //ip地址

              };

 

              struct in_addr{

                    in_addr_t s_addr //整数类型的IP地址

              };

        

         int listen(int sockfd, int backlog);

 

              参一:socket描述符,socket函数的返回值

              参二:用于指定请求但未响应的队列的最大长度

              功能:主要用于将参数sockfd所指向的socket标记为被动的socket,所谓的被动socket就是专门用于响应

                       即将到来的连接请求,不再用于通信(用于接待的socket);

 

         int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

             

              参一:socket描述符,socket函数的返回值

              参二:结构体指针,用于带出客户端的通信地址

              参三:指针类型,用于带出通信地址的大小

              返回:成功返回新的socket描述符,失败返回-1

              功能:主要用于响应客户端的链接请求,主要用于从监听状态socket上悬而未决队列中提取第一个链接请求

                       +并进行处理,处理的方式就是创建一个新的链接成功的socket进行通信

 

         ssize_t recv(int sockfd, void *buf, size_t len, int flags);

    

              参一:socket描述符,accept函数的返回值

              参二:缓冲区首地址,用于保存接收到的数据

              参三:期望接收的数据大小

              参四:接收的标志,默认给0即可

              返回:成功返回实际接收数据的大小,失败返回-1

              功能:主要用于接收参数指定socket中的内容

 

          ssize_tsend(int sockfd, const void *buf, size_t len, int flags);

 

              参一:socket描述符,传递主动socket的描述符

              参二:缓冲区首地址

              参三:期望接收的数据大小

              参四:发送的标志,默认给0即可

              返回:成功返回实际发送数据的大小,失败返回-1

              功能:主要用于向指定的socket发送指定的内容

 

         52     //解决地址被占用的问题

         53

         54     intreuseaddr = 1;

         55

         56     setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &reuseaddr,sizeof(int));

 

      1 //实现基于tcp协议模式的一对多程序_服务端


 2  3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <sys/types.h> 8 #include <sys/socket.h> 9 #include <netinet/in.h>10 #include <arpa/inet.h>11 #include <signal.h>12 13 //定义全局变量记录socket描述符14 15 int sockfd;16 17 void fa(int signum )18 {19     printf("正在关闭服务器.请稍后...\n");20     sleep(3);21     int res = close(sockfd);22     if(-1 == res )23     {24         perror("close");25         exit(-1);26     }27 28     printf("成功关闭服务器\n");29     exit(0);    //终止当前进程30 }31 32 int main(void)33 {34 35     //1.创建socket,使用socket函数36 37     sockfd = socket(AF_INET, SOCK_STREAM, 0);38     if(-1 == sockfd )39     {41         exit(-1);42     }43 44     printf("socket创建成功!\n");45     //2.准备通信地址,使用结构体类型46 47     struct sockaddr_in addr;48     addr.sin_family = AF_INET;49     addr.sin_port = htons(8888);50     addr.sin_addr.s_addr = inet_addr("172.30.4.127");51 52     //解决地址被占用的问题53 54     int reuseaddr = 1;55 56     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,sizeof(int));57 58     //3.绑定socket和通信地址,使用bind函数59     int res = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));60     if(-1 == res )61     {62         perror("bind");63         exit(-1);64     }65 66     printf("绑定成功!\n");67     //4.监听,使用listen函数68 69     res = listen(sockfd, 100);70     if(-1 == res)71     {72         perror("listen");73         exit(-1);74     }75 76     printf("监听成功\n");77 78     //使用信号关闭服务器79     printf("关闭服务器,请按CTRL+C..\n");80     if(SIG_ERR == signal(SIGINT, fa))81     {82         perror("signal");83         exit(-1);84     }85 86     //5.不断地响应客户端的链接要求,accept87 88     while(1)89     {90         struct sockaddr_in recv_addr;91         socklen_t len = sizeof(recv_addr);92         int fd = accept(sockfd, (struct sockaddr *)&recv_addr, &len);93         if(-1 == fd)94         {95             perror("accept");96             exit(-1);97         }98 99         char * ip = inet_ntoa(recv_addr.sin_addr);00         printf("客户端%s链接成功..\n", ip);01 02         //创建子进程为当前客户端服务03         pid_t pid = fork();04         if( -1 == pid )05         {06             perror("fork");07             exit(-1);08         }09 10         //子进程11         if( 0 == pid)12         {13             //设置子进程对信号SIGINT默认处理14             if( SIG_ERR == signal(SIGINT, SIG_DFL))15             {16                 perror("signal");17                 exit(-1);18             }19 20             //关闭接待/被动的socket21             res = close(sockfd);22             if(-1 == res )23             {24                 perror("close");25                 exit(-1);26             }27             //6.针对每个客户端可以不断地进行通信28 29 30             while(1)31             {32                 char buf[100] ={0};33 34                 res = recv(fd, buf, sizeof(buf), 0);35 36                 if( -1 == res )37                 {38                     perror("recv");39                     exit(1);40                 }41 42                 //当客户端发来bye表示下线43                 if( strcmp(buf, "bye") == 0 )44                 {45                     printf("客户端%s已下线!", ip);46                     break;47                 }48 49                 printf("客户端%s发来的消息是:%s\n", ip, buf);50 51                 printf("发送的消息为:");52                 fgets(buf, sizeof(buf), stdin);53 54                 if( strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf)-2] != '\n' )55                 {56                     scanf("%*[^\n]");57                     scanf("%*c");58                 }59 60                 res = send(fd, buf, strlen(buf), 0);61 62                 if( -1 == res )63                 {64                     perror("send");65                     exit(-1);66                 }67             }68 69             //执行break之后跳到了这里70 71             //关闭用于通信的fd72             res = close(fd);73             if(-1 == res )74             {75                 perror("close");76                 exit(-1);77             }78 79             //终止子进程80             exit(0);81         }82 83         //父进程关闭通信fd85         res = close(fd);86         if(-1 == res )87         {88            perror("close");89             exit(-1);90         }93     }95     return 0;96 }




0 0
原创粉丝点击