Linux下编写udp群聊室

来源:互联网 发布:东华软件his系统 编辑:程序博客网 时间:2024/05/27 02:26

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

  • 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);        }}intmain(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;}}intmain(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));                 }}}}