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

相关文章
|
5天前
|
存储 编译器 C语言
c++的学习之路:5、类和对象(1)
c++的学习之路:5、类和对象(1)
21 0
|
20天前
|
存储 C++ 容器
C++STL(标准模板库)处理学习应用案例
【4月更文挑战第8天】使用C++ STL,通过`std:vector`存储整数数组 `{5, 3, 1, 4, 2}`,然后利用`std::sort`进行排序,输出排序后序列:`std:vector<int> numbers; numbers = {5, 3, 1, 4, 2}; std:sort(numbers.begin(), numbers.end()); for (int number : numbers) { std::cout << number << " "; }`
19 2
|
16天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
40 6
|
5天前
|
C++
c++的学习之路:7、类和对象(3)
c++的学习之路:7、类和对象(3)
19 0
|
4天前
|
存储 网络协议 关系型数据库
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
|
5天前
|
C语言 C++
c++的学习之路:4、入门(3)
c++的学习之路:4、入门(3)
18 0
|
5天前
|
编译器 C++
c++的学习之路:23、多态(2)
c++的学习之路:23、多态(2)
17 0
|
12天前
|
数据采集 API 数据安全/隐私保护
畅游网络:构建C++网络爬虫的指南
本文介绍如何使用C++和cpprestsdk库构建高效网络爬虫,以抓取知乎热点信息。通过亿牛云爬虫代理服务解决IP限制问题,利用多线程提升数据采集速度。示例代码展示如何配置代理、发送HTTP请求及处理响应,实现多线程抓取。注意替换有效代理服务器参数,并处理异常。
畅游网络:构建C++网络爬虫的指南
|
1月前
|
机器学习/深度学习 数据采集 人工智能
m基于深度学习网络的手势识别系统matlab仿真,包含GUI界面
m基于深度学习网络的手势识别系统matlab仿真,包含GUI界面
43 0
|
1月前
|
机器学习/深度学习 算法 计算机视觉
基于yolov2深度学习网络的火焰烟雾检测系统matlab仿真
基于yolov2深度学习网络的火焰烟雾检测系统matlab仿真