UNIX_C 高级编程<六>

网络编程

    

     七层网络模型  `                                                                                                                                                                                                                                                                                                                                                                                                                                                                          

 

         为了实现数据在网络中的传递,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.使用close
	47 
	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.不断地响应客户端的链接要求,accept
	87 
	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             //关闭接待/被动的socket
	21             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             //关闭用于通信的fd
	72             res = close(fd);
	73             if(-1 == res )
	74             {
	75                 perror("close");
	76                 exit(-1);
	77             }
	78 
	79             //终止子进程
	80             exit(0);
	81         }
	82 
	83         //父进程关闭通信fd
	85         res = close(fd);
	86         if(-1 == res )
	87         {
	88            perror("close");
	89             exit(-1);
	90         }
	93     }
	95     return 0;
	96 }




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值