网络编程(作业)8.21

1. 完善服务器代码, 做到给所有客户端发送数据;

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

int main(int argc, const char* argv[]) 
{
   //1.创建套接字
    //AF_INET为协议族 - ipv4协议族
    //STREAM 流式套接字 - TCP
    //0 -自动匹配协议 
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0)
    {
      perror("socket is err:");
      return -1;
    }
    printf("sockfd = %d\n",sockfd); //3


    int opt = 1;
    //设置套接字的属性: 将套接字属性设置为 端口可重复使用的功能
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));


    //2.绑定套接字 bind
    struct sockaddr_in saddr,caddr;
    saddr.sin_family = AF_INET;
    //主机字节序的端口号 转换为 网络字节序
    saddr.sin_port = htons(atoi(argv[1])); //atoi 将字符串 转换为 整形数
    //0.0.0.0 监听本机所有IPV4的地址
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    
    int len = sizeof(caddr);

    //绑定 填充的结构体属性 到 sockfd 套接字上
    if(bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0)
    {
      perror(" bind is err:");
      return -1;
    }
    printf("sockaddr = %d   sockaddr_in = %d\n",sizeof(struct sockaddr),sizeof(struct sockaddr_in));


    //3. 监听 - 创建监听队列
    if(listen(sockfd,5) < 0)
    {
       perror("listen is err:");
       return -1;
    }
    
    //1.创建表
    fd_set readfds,tempfds;
    //2.清空表
    FD_ZERO(&readfds);
    //3.将关心文件描述符添加表
    FD_SET(sockfd,&readfds);
    FD_SET(0,&readfds);
  
     int maxfd = sockfd;

     char buf[128];
    while(1)
    {
        //将原表的内容 拷贝到 备份表
        tempfds = readfds;
        //4.select轮训检测
       select(maxfd+1,&tempfds,NULL,NULL,NULL);


       //5.判断文件描述 在表内是否有事件
       if(FD_ISSET(0,&tempfds))
       {
           //键盘输入
        fgets(buf,sizeof(buf),stdin);
        for(int i=4; i<=maxfd; i++)
        {
          if(FD_ISSET(i,&tempfds))
          {
            if(buf[strlen(buf)-1] == '\n')
                buf[strlrn(buf)-1] = '\0';
                //发送给所有客户端
                send(i,buf,sizeof(buf),0);
          }
        } 
           
       }
       if(FD_ISSET(sockfd,&tempfds)) //链接的文件描述符
       {
          //要建立通信了
          int acceptfd = accept(sockfd,(struct sockaddr *)&caddr,&len);
          if(acceptfd < 0)
          {
            perror("accept is err:");
            return -1;
          }
          //将通信的文件描述符 添加到原表内
           FD_SET(acceptfd,&readfds);
          
          // 只有新的通信的文件描述符 > 最大文件描述符下值, 才会让maxfd的值改变
          if(acceptfd > maxfd)
          {
             maxfd = acceptfd;
          }      
       }
       for(int i = 4; i <= maxfd; i++)
       {
         if(FD_ISSET(i,&tempfds))
         {
             int ret = recv(i,buf,sizeof(buf),0);
             if(ret < 0)
             {
               perror("recv is err:");
               return -1;
             }
             else if(ret == 0)
             {
               printf("client %d is exit\n",i);
               close(i);

               //删除表内的已退出的文件描述符
               FD_CLR(i,&readfds);
               //maxfd
               if(i == maxfd)
               {
                maxfd--;
               }
             }
             else 
             {
               printf("client:%d   : %s\n",i,buf);
             }
         }
       }

    }
    
    close(sockfd);  
    return 0;
}

补充部分截图:

2. 利用select 实现 TCP客户端的全双工;

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;

int main(int argc, const char* argv[]) 
{
    //1.创建套接字
    //AF_INET为协议族 - ipv4协议族
    //STREAM 流式套接字 - TCP
    //0 -自动匹配协议 
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        perror("sockfd is error:");
        return -1;
    }
    //设置套接字的属性: 将套接字属性设置为 端口可重复使用的功能
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));


    //2.套接字 
    struct sockaddr_in saddr,caddr;
    saddr.sin_family = AF_INET;
    //主机字节序的端口号 转换为 网络字节序
    saddr.sin_port = htons(atoi(argv[1])); //atoi 将字符串 转换为 整形数
    //0.0.0.0 监听本机所有IPV4的地址
    saddr.sin_addr.s_addr = inet_addr(argv[2]);

    if(connect(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0)
    {
        perror("connect is error:");
        return -1;
    }
    //1.创建表
    fd_set readfds,tempfds;
    //2.清空表
    FD_ZERO(&readfds);
    //3.将关心文件描述符添加表
    FD_SET(sockfd,&readfds);
    FD_SET(0,&readfds);
  
     int maxfd = sockfd;

     char buf[128];
     while(1)
     {
        //将原表的内容 拷贝到 备份表
        tempfds = readfds;
        //4.select轮训检测
       if(select(maxfd+1,&tempfds,NULL,NULL,NULL) < 0)
       {
            perror("select is error:");
            return -1;
       }

       //5.判断文件描述 在表内是否有事件
       if(FD_ISSET(sockfd,&tempfds))
       {
        int res = recv(sockfd,buf,sizeod(buf),0);
        if (res < 0)
        {
           perror("res is error:");
           break;
        }
        else if(res == 0)
        {
            printf("res is exit:\n");
        }
        else
        {
            printf("client: %s\n",buf);
        }
        
       }
       if(FD_ISSET(0,&tempfds))
       {
        fgets(buf,sizeof(buf),stdin);
        if(buf[strlen(buf)-1] == '\n')
            buf[strlen(buf)-1] = '\0';
            send(sockfd,buf,sizeof(buf),0);
       }
     }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值