作业-day-240525

UDP聊天系统

服务器端

#include <myhead.h>
#define SER_IP "192.168.2.8"
#define SER_PORT 8888

typedef struct Node{
        char username[20];
        struct sockaddr_in cin;
        struct Node *next;
}node,*node_p;

//创建头结点
node_p create_head()
{
        node_p H=(node_p)malloc(sizeof(node)); //申请空间
        if(H==NULL)
        {
                printf("空间申请失败\n");
                return NULL;
        }
        H->next=NULL;
        return H;
}

//新建结点
node_p create_node(char *username,struct sockaddr_in cin)
{
        node_p new=(node_p)malloc(sizeof(node));
        if(new==NULL)
        {
                printf("空间申请失败\n");
                return NULL;
        }
        strcpy(new->username,username);
        new->cin=cin;
        return new;
}

int empty_ch(node_p H)
{
        //入参判空
        if(H==NULL)
        {
                printf("入参为空\n");
                return -1;
        }
        return H->next==NULL?1:0;
}

//尾插
void insert_tail(node_p H,char *username,struct sockaddr_in cin)
{
        //判空
        if(H==NULL)
        {
                printf("入参为空\n");
                return;
        }
        node_p p=H;
        //循环到链表尾
        while(p->next != NULL)
                p=p->next;
        node_p new=create_node(username,cin);
        new->next=p->next;
        p->next=new;
}

//查找指定的客户端结点
char *find(node_p H,struct sockaddr_in cin)
{
        if(H==NULL)
        {
                printf("入参为空\n");
                return NULL;
        }
        if(H->next==NULL)
        {
                printf("链表为空\n");
                return NULL;
        }

        //循环,找出IP、结点相同的结点名称
        node_p p=H->next;
        while(p != NULL)
        {
                if((p->cin.sin_addr.s_addr == cin.sin_addr.s_addr) && (p->cin.sin_port == cin.sin_port))
                        return p->username;
                p=p->next;
        }
        return NULL;
}

//删除指定的客户端信息结点
void delete_node(node_p H,struct sockaddr_in cin)
{
        //判空
        if(H==NULL)
        {
                printf("入参为空\n");
                return;
        }
        if(H->next==NULL)
        {
                printf("链表为空\n");
                return;
        }

        node_p p=H->next;
        //循环找到指定结点的上一个结点
        while((p->next->cin.sin_addr.s_addr != cin.sin_addr.s_addr) || (p->cin.sin_port != cin.sin_port))
                p=p->next;
        //保存要删除的结点
        node_p del=p->next;
        //将上一个结点指向下下个结点地址
        p->next=p->next->next;
        //释放要删除的结点空间
        free(del);
}


int main(int argc, const char *argv[])
{
        //创建UDP套接字
        int sfd = socket(AF_INET,SOCK_DGRAM,0);
        if(sfd == -1)
        {
                perror("socket error");
                return -1;
        }
        printf("%d\n",sfd);
        //绑定IP地址和端口
        //初始化结构体
        struct sockaddr_in sin;
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = inet_addr(SER_IP);
        sin.sin_port = htons(SER_PORT);
        if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1)
        {
                perror("bind error");
                return -1;
        }
        printf("bind success\n");

        //使用poll函数完成任务并发
        struct pollfd pfd[2];
        pfd[0].fd=0;     //检查0号文件描述符
        pfd[0].events=POLLIN;  //检查读事件
        pfd[1].fd=sfd;   //检查sfd号文件描述符
        pfd[1].events=POLLIN;  //检查读事件

        //创建头结点
        node_p H=create_head();


        //信息收发容器
        char rbuf[128]="";
        char wbuf[128]="";
        //准备获取对端地址信息
        struct sockaddr_in cin;
        socklen_t addrlen=sizeof(cin);

        while(1)
        {
                //poll函数,2个文件描述符,永久阻塞
                poll(pfd,2,-1);

                //判断是否为0的事件,服务器向所有客户端发送信息
                if(pfd[0].revents == POLLIN)
                {
                        bzero(wbuf,sizeof(wbuf));
                        fgets(wbuf,sizeof(wbuf),stdin);
                        wbuf[strlen(wbuf)-1]=0;

                        //格式化要发送的信息
                        char str[256]="";
                        snprintf(str,sizeof(str),"服务器:%s",wbuf);
                        //将信息发送给所有客户端
                        if(empty_ch(H) == 1)
                        {
                                printf("还未有用户加入聊天室\n");
                                return -1;
                        }
                        node_p p=H->next;
                        while(p != NULL)
                        {
                                if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
                                {
                                        perror("sendto error");
                                        return -1;
                                }
                                p=p->next;
                        }
                        //记录服务器操作
                        printf("服务器[%s:%d]发送了一条消息\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));
                }

                //判断是否为sfd的事件,客户端间通信
                if(pfd[1].revents == POLLIN)
                {
                        bzero(rbuf,sizeof(rbuf));
                        //接收客户端信息
                        if(recvfrom(sfd,rbuf,sizeof(rbuf),0,(struct sockaddr*)&cin,&addrlen) == -1)
                        {
                                perror("recvfrom error");
                                return -1;
                        }
                        //判断客户端地址是否已接入
                        char *ret=find(H,cin);
                        //用户首次接入
                        if(ret == NULL)
                        {
                                //插入用户名和地址信息
                                insert_tail(H,rbuf,cin);
                                //服务器端输出客户端加入的信息
                                printf("%s [%s:%d]加入了聊天室\n",rbuf,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));

                                //将客户端加入的信息发送给所有客户端
                                char str[256]="";
                                snprintf(str,sizeof(str),"----%s加入了聊天室----",rbuf);
                                node_p p=H->next;
                                while(p->next != NULL)
                                {
                                        if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
                                        {
                                                perror("sendto error");
                                                return -1;
                                        }
                                        p=p->next;
                                }
                        }
                        //用户先前已接入
                        else if(ret>0)
                        {
                                //用户退出请求
                                if(strcmp(rbuf,"quit") == 0)
                                {
                                        //服务器端输出客户端退出的信息
                                        printf("%s [%s:%d]退出了聊天室\n",rbuf,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));

                                        //将退出信息发送给服务器端和所有客户端
                                        char str[256]="";
                                        sprintf(str,"----%s退出了聊天室----",ret);
                                        node_p p=H->next;
                                        while(p != NULL)
                                        {
                                                if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
                                                {
                                                        perror("sendto error");
                                                        return -1;
                                                }
                                                p=p->next;
                                        }
                                        //删除链表中删除对应的结点
                                        delete_node(H,cin);
                                }
                                //用户聊天信息
                                else
                                {
                                        //将信息发送给其他用户
                                        char str[256]="";
                                        snprintf(str,sizeof(str),"%s:%s",ret,rbuf);
                                        node_p p=H->next;
                                        while(p != NULL)
                                        {
                                                //跳过自己
                                                if((p->cin.sin_addr.s_addr == cin.sin_addr.s_addr) && (p->cin.sin_port == cin.sin_port))
                                                {
                                                        p=p->next;
                                                        continue;
                                                }
                                                //将消息发送给其他用户
                                                if(sendto(sfd,str,strlen(str),0,(struct sockaddr*)&p->cin,sizeof(p->cin)) == -1)
                                                {
                                                        perror("sendto error");
                                                        return -1;
                                                }
                                                p=p->next;
                                        }
                                        //服务器端记录用户的操作
                                        printf("%s [%s:%d]发送了一条消息\n",ret,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
                                }
                        }
                }
        }

        close(sfd);
        return 0;
}

客户端

#include <myhead.h>
#define SER_IP "192.168.2.8"
#define SER_PORT 8888


int  main(int argc, const char *argv[])
{
        //创建UDP套接字
        int cfd = socket(AF_INET,SOCK_DGRAM,0);
        if(cfd == -1)
        {
                perror("socket error");
                return -1;
        }

        struct sockaddr_in sin;
        sin.sin_family=AF_INET;
        sin.sin_addr.s_addr=inet_addr(SER_IP);
        sin.sin_port=htons(SER_PORT);

        struct pollfd pfd[2];
        pfd[0].fd=0;
        pfd[0].events=POLLIN;
        pfd[1].fd=1;
        pfd[1].events=POLLIN;
        printf("请输入昵称:>>>>>");
        fflush(stdout);

        char wbuf[128]="";
        char rbuf[256]="";

        while(1)
        {
                bzero(wbuf,sizeof(wbuf));
                bzero(rbuf,sizeof(rbuf));

                poll(pfd,2,-1);
                //客户端输入信息
                if(pfd[0].revents == POLLIN)
                {
                        fgets(wbuf,sizeof(wbuf),stdin);
                        wbuf[strlen(wbuf)-1]='\0';
                        //将信息发送给服务器
                        if(sendto(cfd,wbuf,strlen(wbuf),0,(struct sockaddr*)&sin,sizeof(sin)) == -1)
                        {
                                perror("sendto error");
                                return -1;
                        }
                        //客户端退出
                        if(strcmp(wbuf,"quit") == 0)
                        {
                                close(cfd);
                                return 0;
                        }
                }
                //客户端接收信息
                if(pfd[1].revents == POLLIN)
                {
                        if(recvfrom(cfd,rbuf,sizeof(rbuf),0,NULL,NULL) == -1)
                        {
                                perror("recvfrom error");
                                return -1;
                        }
                        printf("%s\n",rbuf);
                }
        }
        close(cfd);

        return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值