C/C++网络编程基础知识超详细讲解第三部分(系统性学习day13)

简介: C/C++网络编程基础知识超详细讲解第三部分(系统性学习day13)

一、并发服务器

1.进程并发服务器

消耗资源大,每连接进来一个客户端,你就要去开辟进程去服务那个客户端

           fork()

           举例:

if(fork()==0)  //子进程模块,不影响主进程中不断接收客户端连接
                        {
                            zhuanfa(&cfd);    
                        }

实例代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
// struct sockaddr_in client;
// int len = sizeof(client);
 
void *zhuanfa(void *arg)
{
  int ret;
  int fd = *(int *)arg;
  char buf[1024];//接信息
  char buf1[50] = "猖狂,北伐!";
  while(1)
  {
 
    bzero(buf,sizeof(buf));
    ret = recv(fd,buf,sizeof(buf),0);
    if(0 == ret)
    {
      printf("客户%d离开了\n",fd);
      close(fd);
      return NULL;
    }else
     {
      printf("客户%d:%s\n",fd,buf); 
      // printf("客户%d进来了,IP地址为%s,端口号为%d\n",fd,inet_ntoa(client.sin_addr),ntohs(client.sin_port));
      send(fd,buf1,strlen(buf1),0); 
     }  
  }
  return NULL;
}
 
int main(void)
{
  //socket
  int serfd = socket(AF_INET,SOCK_STREAM,0);
  if(0>serfd)
  {
    perror("socket");
    return -1;
  }
  //bind
  struct sockaddr_in ser;//netinet/in.h
  ser.sin_family = AF_INET;
  ser.sin_port = htons(8888);
  ser.sin_addr.s_addr = inet_addr("192.168.10.5");
  if(bind(serfd,(struct sockaddr *)&ser,sizeof(ser))<0)
  {
    perror("bind");
    return -1;
  } 
  //listen
  listen(serfd,8);
  //accept
  int cfd;
  pthread_t a;
  while(1)
  {
    //不断接受不同的客户端,并分配一个服务员给客户对接,在线程进行通信
    cfd = accept(serfd,NULL,NULL);//accept保存客户信息到client
    // pthread_create(&a,NULL,zhuanfa,&cfd);
    // pthread_detach(a);
    if(fork()==0)
    {
      zhuanfa(&cfd);  
    }
  }
  return 0;
}

2.线程并发服务器

占用资源资源比较小,代码维护起来困难

           pthread_create  //线程的创建

           pthread_detach    //给创建线程能自动收尸的能力

           不自动:pthread_join

           

           printf("客户%d进来了,IP地址为%s,端口号为%d\n",fd,inet_ntoa(client.sin_addr),ntohs(client.sin_port));

           inet_ntoa(client.sin_addr) //网络二进制转回点分十进制

           ntohs(client.sin_port) //大端转小端

实例代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
struct sockaddr_in client;
int len = sizeof(client);
void *zhuanfa(void *arg)
{
  int ret;
  int fd = *(int *)arg;
  char buf[1024];//接信息
  char buf1[50] = "注意绿色上网!";
  while(1)
  {
 
    bzero(buf,sizeof(buf));
    ret = recv(fd,buf,sizeof(buf),0);
    if(0 == ret)
    {
      printf("客户%d离开了\n",fd);
      close(fd);
      return NULL;
    }else
     {
      printf("客户%d:%s\n",fd,buf); 
      printf("客户%d进来了,IP地址为%s,端口号为%d\n",fd,inet_ntoa(client.sin_addr),ntohs(client.sin_port));
      send(fd,buf1,strlen(buf1),0); 
     }  
  }
  return NULL;
}
 
int main(void)
{
  //socket
  int serfd = socket(AF_INET,SOCK_STREAM,0);
  if(0>serfd)
  {
    perror("socket");
    return -1;
  }
  //bind
  struct sockaddr_in ser;//netinet/in.h
  ser.sin_family = AF_INET;
  ser.sin_port = htons(8888);
  ser.sin_addr.s_addr = inet_addr("192.168.10.5");
  if(bind(serfd,(struct sockaddr *)&ser,sizeof(ser))<0)
  {
    perror("bind");
    return -1;
  } 
  //listen
  listen(serfd,8);
  //accept
  int cfd;
  pthread_t a;
  while(1)
  {
    //不断接受不同的客户端,并分配一个服务员给客户对接,在线程进行通信
    cfd = accept(serfd,(struct sockaddr *)&client,&len);//accept保存客户信息到client
    pthread_create(&a,NULL,zhuanfa,&cfd);
    pthread_detach(a);
    
  }
  return 0;
}

二、域通信

优点:没网情况下照样能用客户端与服务器代码测试,模仿TCP/UDP

局限性:不能跨主机,只用于网络环境苛刻下的代码测试

区别:

               域通信:

                   struct sockaddr_un        <sys/un.h>

                       struct sockaddr_un{

                           sa_family_t   sin_family;   //地址族

                           char     sun_path[108];      //s套接字的路径千万要用strcpy赋值

                       };

                   s套接字,在bind后运行执行文件它就出现

域通信TCP实例代码如下:

服务器:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
#include <sys/select.h>
int main(void)
{
  //socket(变动)
  int sockfd = socket(AF_UNIX,SOCK_STREAM,0);//注意AF_UNIX
  if(0>sockfd)
  {
    perror("socket");
    return -1;
  }
  
  //bind(变动)
  struct sockaddr_un server;
  server.sun_family = AF_UNIX;
  strcpy(server.sun_path,"DJ");
  bind(sockfd,(struct sockaddr *)&server,sizeof(server));
  
  //listen
  listen(sockfd,8);
  
  //多路复用select
  int max = 0;
  int ret,cfd;//标志
  fd_set rfds;//读集合
  char buf[30];
  while(1)
  {
    FD_ZERO(&rfds);
    FD_SET(0,&rfds);
    FD_SET(sockfd,&rfds);
    max = sockfd;
    if(cfd>sockfd)//第一遍还没连接,这个判断没有作用
    {
      max=cfd;
      FD_SET(cfd,&rfds);
    }
    select(max+1,&rfds,NULL,NULL,NULL);
    if(FD_ISSET(0,&rfds))
    {
      bzero(buf,sizeof(buf));
      printf("0文件描述符触发\n");
      scanf("%s",buf);
      printf("键盘输入:%s\n",buf);
      if(cfd>3)//说明有人连接,改变了cfd一开始的值
      {
        send(cfd,buf,strlen(buf),0);
      }
    }else if(FD_ISSET(sockfd,&rfds))
     {
       cfd = accept(sockfd,NULL,NULL);
      if(0>cfd)
      {
        perror("accept");
        return -1;
      }
       printf("有客户连接进来了!\n");
     }else
      {
        bzero(buf,sizeof(buf));
        ret = recv(cfd,buf,sizeof(buf),0);
        if(0 == ret)
        {
        perror("recv");
        return -1; 
        }else
         {
           printf("客户说:%s\n",buf);
         } 
      }
  }
 
  return 0;
}

客户端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
void *(recv_msg)(void *arg)
{
  int ret;
  int fd = *(int *)arg;
  char buf[50];
  while(1)
  {
    bzero(buf,sizeof(buf));
    ret = recv(fd,buf,sizeof(buf),0);
    if(0>ret)
    {
      perror("recv");
      return NULL;
    }else if(0 == ret)
     {
       printf("服务器离开了\n");
       return NULL;
     }else
       printf("服务器说:%s\n",buf);
  }
  return NULL;
}
int main(int argc,char *argv[])
{
  //变量区
  int clifd,ret;
  char buf[1024];
  pthread_t pid;
  
  //1>进行传参错误判断
  if(argc<2)//你运行时输入的个数   ./x a b 
  {
    printf("请输入<./可执行> <S_name> \n"); 
    return -1;
  }
  //2>创建socket套接字
  clifd = socket(AF_UNIX,SOCK_STREAM,0);
  if(clifd<0)
  {
    perror("socket");
    return -1;
  }
  printf("创建出的socket的值为%d\n",clifd);
  //3>声明s套接字
  struct sockaddr_un server;
  server.sun_family = AF_UNIX;
  strcpy(server.sun_path,(argv[1]));
#if 0
  if(bind(clifd,(struct sockaddr *)&server,sizeof(server))<0)
  {
    perror("bind");
    return -1;
  }
 
  //4>监听
  if(listen(clifd,8)<0)
  {
    perror("listen");
    return -1;
  }
  printf("监听已启动,保护服务器中^-^\n");
#endif
  //5>主动连接服务器
  if(connect(clifd,(struct sockaddr *)&server,sizeof(server))<0)
  {
    perror("connect");
    return -1;
  }
  printf("成功连接!\n");
  //开辟线程
  pthread_create(&pid,NULL,recv_msg,&clifd);
  pthread_detach(pid);
  
  //6>收发数据
  while(1)
  {
    bzero(buf,sizeof(buf));
    scanf("%s",buf);
    send(clifd,buf,strlen(buf),0);
  }
  //7>关闭套接字
  close(clifd);
  return 0;
}

补充说明:

注意:如果bind的错误提示,说地址已经占用

                   就用remove();清掉自己绑定的s套接字,再运行就没有

                   AF_UNIX

                       进程间通信有七种

                           早期:

                               1>无名管道

                               2>有名管道

                               3>信号

                           系统:

                               4>消息队列

                               5>共享内存

                               6>信号量

                           网络编程:

                               7>s套接字

               正常:

                   struct sockaddr_in

                   网络属性(IP地址和端口号)

                   AF_INET

三、广播与组播(UDP)

1.广播

         看图

       允许发送的广播的属性怎么设置

           #include<sys/types.h>

           #include<sys/socket.h>

           setsockopt

               int setsockopt(int sockfd,int level,int optname,const void * optval,socklen_t optlen);

               功能:

                   设置套接字的属性

               参数:

                   sockfd:套接字

                   level:等级

                   optname:属性名字

                   optval:属性的值

                   optlen:属性的长度

               返回值:

                   成功为0

                   失败返回-1,并设置错误码

           举例:

               1>允许发送的广播的属性

               int on = 1;//1>为生效值,0>不生效

               setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));

               2>允许重复用

               int on = 1;//1>为生效值,0>不生效

               setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))

实例代码如下:

sendto:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
int main(void)
{
  int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(0>sockfd)
  {
    perror("socket");
    return -1;
  }
  //设置发送广播属性
  int on = 1;//1>为生效值,0>不生效
  setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
  
  struct sockaddr_in gb;
  gb.sin_family = AF_INET;
  gb.sin_port = htons(10086);
  gb.sin_addr.s_addr = inet_addr("192.168.10.255");
#if 0
  if(bind(sockfd,(struct sockaddr *)&gb,sizeof(gb))<0)
  {
    perror("bind");
    return -1;
  }
#endif  
  char buf[1024];
  // int addrlen = sizeof(gb);
  while(1)
  {
    bzero(buf,sizeof(buf));
    scanf("%s",buf);
    sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&gb,sizeof(gb));
    // printf("广播:%s\n",buf);
  }
  return 0;
}

recvfrom:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
int main(void)
{
  int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(0>sockfd)
  {
    perror("socket");
    return -1;
  }
  
  struct sockaddr_in gb;
  gb.sin_family = AF_INET;
  gb.sin_port = htons(10086);
  gb.sin_addr.s_addr = inet_addr("192.168.10.255");
  if(bind(sockfd,(struct sockaddr *)&gb,sizeof(gb))<0)
  {
    perror("bind");
    return -1;
  }
  
  char buf[1024];
  int addrlen = sizeof(gb);
  while(1)
  {
    bzero(buf,sizeof(buf));
    recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&gb,&addrlen);
    printf("广播:%s\n",buf);
  }
  return 0;
}

2.组播

特定的广播,进一步细化成员

           看图

           多播组只有一个人,为单播;人多,就多播。

     

optval,————》ip-mreq{}     //与stuct sockaddr_in一个头文件netinet/in.h
            struct ip_mreq 
            {
                 struct  in_addr  imr_multiaddr;    //    组播地址    
                 struct  in_addr  imr_interface;    //自己linux的ip地址
            };
            struct in_addr{
                In_addr_t  s_addr;    //32位IPv4地址
            };
            举例:加入多播组
            struct ip_mreq zb;
            zb.imr_multiaddr.s_addr = inet_addr("233.233.233.233");
            zb.imr_interface.s_addr = inet_addr("192.168.10.5");
            setsockopt(sockfd,IPPROTO_IP,SO_ADD_MEMBERSHIP,&zb,sizeof(zb));

实例代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
int main(void)
{
  //1.socket
  int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(0>sockfd)
  {
    perror("socket");
    return -1;
  }
  //2.运行发送广播
  int on = 1;//1>为生效值,0>不生效
  setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
  //3.声明组播地址
  struct sockaddr_in zb;
  zb.sin_family = AF_INET;
  zb.sin_port = htons(9898);
  zb.sin_addr.s_addr = inet_addr("233.233.233.233");
  // if(bind(sockfd,(struct sockaddr *)&zb,sizeof(zb))<0)
  // {
    // perror("bind");
    // return -1; 
  // }
  //3.加入多播组
  // struct ip_mreq zb;
  // zb.imr_multiaddr.s_addr = inet_addr("233.233.233.233");
  // zb.imr_interface.s_addr = inet_addr("192.168.10.5");
  // setsockopt(sockfd,IPPROTO_IP,SO_ADD_MEMBERSHIP,&zb,sizeof(zb));
  //4.接收数据
  char buf[30];
  // int len = sizeof(zb);
  while(1)
  {
    bzero(buf,sizeof(buf));
    scanf("%s",buf);
    sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&zb,sizeof(zb));
    // printf("S:%s\n",buf);
  }
  return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/un.h>
int main(void)
{
  //1.socket
  int sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(0>sockfd)
  {
    perror("socket");
    return -1;
  }
  //2.bind组播地址
  struct sockaddr_in zb;
  zb.sin_family = AF_INET;
  zb.sin_port = htons(9898);
  zb.sin_addr.s_addr = inet_addr("233.233.233.233");
  if(bind(sockfd,(struct sockaddr *)&zb,sizeof(zb))<0)
  {
    perror("bind");
    return -1;  
  }
  //3.加入多播组
  struct ip_mreq db;
  db.imr_multiaddr.s_addr = inet_addr("233.233.233.233");
  db.imr_interface.s_addr = inet_addr("192.168.10.5");
  setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&db,sizeof(db));
  //4.接收数据
  char buf[30];
  int len = sizeof(zb);
  while(1)
  {
    bzero(buf,sizeof(buf));
    recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&zb,&len);
    printf("S:%s\n",buf);
  }
  return 0;
}

四、图解如下


总结

关于C/C++网络编程基础知识超详细讲解第二部分的详解,懒大王就先分享到这里了,如果你认为这篇文章对你有帮助,请给懒大王点个赞点个关注吧,如果发现什么问题,欢迎评论区留言!!💕💕 💕

相关文章
|
17天前
|
编解码 安全 Linux
网络空间安全之一个WH的超前沿全栈技术深入学习之路(10-2):保姆级别教会你如何搭建白帽黑客渗透测试系统环境Kali——Liinux-Debian:就怕你学成黑客啦!)作者——LJS
保姆级别教会你如何搭建白帽黑客渗透测试系统环境Kali以及常见的报错及对应解决方案、常用Kali功能简便化以及详解如何具体实现
|
17天前
|
安全 网络协议 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-1):主动信息收集之ping、Nmap 就怕你学成黑客啦!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-1):主动信息收集之ping、Nmap 就怕你学成黑客啦!
|
17天前
|
网络协议 安全 NoSQL
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
|
17天前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
|
21天前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
17天前
|
人工智能 安全 Linux
网络空间安全之一个WH的超前沿全栈技术深入学习之路(4-2):渗透测试行业术语扫盲完结:就怕你学成黑客啦!)作者——LJS
网络空间安全之一个WH的超前沿全栈技术深入学习之路(4-2):渗透测试行业术语扫盲完结:就怕你学成黑客啦!)作者——LJS
|
17天前
|
安全 大数据 Linux
网络空间安全之一个WH的超前沿全栈技术深入学习之路(3-2):渗透测试行业术语扫盲)作者——LJS
网络空间安全之一个WH的超前沿全栈技术深入学习之路(3-2):渗透测试行业术语扫盲)作者——LJS
|
17天前
|
SQL 安全 网络协议
网络空间安全之一个WH的超前沿全栈技术深入学习之路(1-2):渗透测试行业术语扫盲)作者——LJS
网络空间安全之一个WH的超前沿全栈技术深入学习之路(1-2):渗透测试行业术语扫盲)作者——LJS
|
17天前
|
网络协议 安全 算法
网络空间安全之一个WH的超前沿全栈技术深入学习之路(9-2):WireShark 简介和抓包原理及实战过程一条龙全线分析——就怕你学成黑客啦!
实战:WireShark 抓包及快速定位数据包技巧、使用 WireShark 对常用协议抓包并分析原理 、WireShark 抓包解决服务器被黑上不了网等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!