Linux下编写udp群聊室

这个正好是我的一个课堂上机小练习,为了实现这个功能,我们需要了解一下几个知识点

  • udp发送和接受数据的过程
  • select语句功能
  • 如何开启一个线程
  • *如何传输结构体struct

一.udp发送数据和接受数据流程

这里写图片描述
无论是客户端还是服务器端,刚开始都需要向系统申请套接字socket,然后通过socket来实现发送和接受消息,只不过服务器端需要把该套接字绑定到某个端口通过调用bind()函数(很多书上说bind()函数公开自己所要监听的端口),其实客户端也是可以执行bind()函数来指明发送数据的端口,不过一般不写,系统会自动分配一个端口给它。总的梳理一下
客户端:申请socket——通过该socket发送数据和接受数据
服务器:申请socket——bind()端口——调用recvfrom()阻塞

二.select语句功能
#include<sys/select.h>
#include<sys/time.h>
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)

先说一下select的功能:这属于IO复用模型,进程会受阻与select调用,select同时监听多个套接字,只要有一个套接字响应了,就不会阻塞,可以执行你想要的处理过程,简单一点就是一个监听多个socket功能。
看一下它的参数
maxfdp1:指定带监听的描述符个数,它的值是待测试的最大描述符加1,换句话说调用socket函数返回int参数就是socket文件描述符
read_set,read_set,exceptset:就是我们要监听的socket文件描述符的集合,一个读,一个写,一个异常
timeout:就是指定监听时长(null代表永久)

三.开启线程
#include<pthread.h>
int pthread_create(pthread_t *restrict tidp,
                   const pthread_attr_t *restrict attr,
                   void *(*start_rtn)(void), 
                   void *restrict arg);)

第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。如果函数不需要参数变成NULL

四.传输结构体

因为socket只能传输字符串,所以我们必须把struct转成char数组才能传输,接收端也只能char数组接受再转成结构体
使用memcpy将文件、结构体、数字等,可以转换为char数组,之后进行传输,接收方在使用memcpy将char数组转换为相应的数据。

void * memcpy ( void * destination, const void * source,size_t num );

 函数说明:从source指向的地址开始拷贝num个字节到以destination开始的地址。其中destination与source指向的数据类型无关。

五.项目代码

说明:因为我是复用老师给的代码中几个函数Sendto和Recv函数,所以需要替换还有头文件需要自己添加一下
客户端

#include    "unp.h"
#include<string.h>
struct fun_para{
 int port;
};
typedef struct{
  char username[10];
  char msg_buf[1024];
  int type;
  struct sockaddr_in address;
}MSG;
static void func(void * para){
   struct fun_para* kk=(struct fun_para *)para;
   int port=kk->port;
   int s=socket(AF_INET,SOCK_DGRAM,0);
   struct sockaddr_in serv;
   bzero(&serv,sizeof(serv));
   serv.sin_family=AF_INET;
   serv.sin_addr.s_addr=htonl(INADDR_ANY);
   serv.sin_port=htons(port);
   int sin_len=sizeof(serv);
   bind(s,(struct sockaddr *)&serv,sizeof(serv));
   if(s==-1)printf("create socket erro:");
    for(;;){
               char recvBuf[1200]={0};
              MSG *client_msg=(MSG*)malloc(sizeof(MSG));
                 int kk=recvfrom(s,recvBuf,1200,0,(SA*)&serv,&sin_len);
                 memcpy(client_msg,recvBuf,sizeof(MSG));
                 printf("username: %smessage:%s\n",client_msg->username,client_msg->msg_buf);
        }
}

int
main(int argc, char **argv)
{
    int sockfd;
     struct sockaddr_in servaddr;
        pthread_t tid;
        char sendline[1024];
        char username[10];
    if (argc != 3)
        err_quit("usage: tcpcli <IPaddress> <Port>");
    sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(9877);
    Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
        sockfd=Socket(AF_INET,SOCK_DGRAM,0);
        bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
        struct fun_para para;
        para.port=atoi(argv[2]);
         struct sockaddr_in serv;
        bzero(&serv,sizeof(serv));
        serv.sin_family=AF_INET;
        serv.sin_addr.s_addr=htonl(INADDR_ANY);
        serv.sin_port=htons(atoi(argv[2]));
        printf("client port: %d\n",atoi(argv[2]));
        printf("input your name\n");
       int flag=0;
       //注册消息
       // Sendto(sockfd,&msg,sizeof(struct MSG),0,(SA*)&servaddr,sizeof(servaddr));
        if(pthread_create(&tid,NULL,&func ,&para)!=0){
             printf("thread_create Failed\n");
          } 
        while(Fgets(sendline,1024,stdin)!=NULL){
            MSG *send=(MSG*)malloc(sizeof(MSG));
           if(flag==0){
              strcpy(send->username,sendline);
              strcpy(send->msg_buf,sendline);
              strcpy(username,sendline);
              send->type=0;
              flag++;
              }else{
               strcpy(send->msg_buf,sendline);
               strcpy(send->username,username);
               send->type=1;
               }
           send->address=serv;
           char information[1200]={0};
           memcpy(information,send,sizeof(MSG));
           Sendto(sockfd,information,sizeof(information),0,(SA*)&servaddr,sizeof(servaddr));
         }
    //str_cli(stdin, sockfd);       /* do it all */
    exit(0);
}

服务器

#include "unp.h"
 typedef struct {
  char username[10];
  char msg_buf[1024];
  int type;
  struct sockaddr_in address;
}MSG;
// 建立一个链表存储客户端的功能
typedef struct client{
    struct sockaddr_in ct_addr;
    struct client* next;
}CNODE,*pCNODE;
void list_insert(pCNODE *phead,pCNODE p){
     p->next=*phead;
     *phead=p;
}
void msg_brocast(int send_sockfd,char* msg,pCNODE phead){
    while(phead){
        printf("Port: %s %d message:%s ",inet_ntoa((phead->ct_addr).sin_addr),htons((phead->ct_addr).sin_port),msg);
        Sendto(send_sockfd,msg,1200,0,(SA*)&(phead->ct_addr),sizeof(SA));
        phead=phead->next;
}
}
int
main(int argc, char **argv)
{
     int sockfd,send_socket;
     int maxfdp1;
     int select_int;
     struct sockaddr_in addr,cli;
      pCNODE my_list=NULL;
     sockfd=Socket(AF_INET,SOCK_DGRAM,0);
     send_socket=Socket(AF_INET,SOCK_DGRAM,0);
     addr.sin_family=AF_INET;
     addr.sin_port=htons(9877);//绑定端口号9877
     addr.sin_addr.s_addr=htonl(INADDR_ANY);
     Bind(sockfd,(SA*)&addr,sizeof(addr));
     fd_set rset;
     FD_ZERO(&rset);
      while(1){
      FD_SET(sockfd,&rset);
      maxfdp1=sockfd+1;
      select_int = select(maxfdp1,&rset,NULL,NULL,NULL);
      if(select_int<0){
              printf("erro\n");
           return;
       }else if(select_int==0){

               printf("timeout\n");
           }else{
              if(FD_ISSET(sockfd,&rset)){
                 pCNODE pNew =(pCNODE)calloc(1,sizeof(CNODE));
                 printf("receve data\n");
                 char recvBuf[1200]={0};
                  MSG *client_msg=(MSG*)malloc(sizeof(MSG));
                 int lent=sizeof(cli);
                 printf("bigsize is %d\n",sizeof(MSG));
                 int kk=recvfrom(sockfd,recvBuf,1200,0,(SA*)&(cli),&lent);  
                  printf("1address: %s  %d \n",inet_ntoa(cli.sin_addr),htons(cli.sin_port));     
             memcpy(client_msg,recvBuf,sizeof(MSG));
                 pNew->ct_addr=client_msg->address;
                  if(client_msg->type==0){
                        printf("添加新成员\n");
                        list_insert(&my_list,pNew);
                  }else if(client_msg->type==1){
                      msg_brocast(send_socket,recvBuf,my_list);       
                   }
                // printf("IP地址为:%s 端口号为:%d",inet_ntoa(cli.sin_addr),htons(cli.sin_port));
                 }
}
}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值