基于UDP的网络聊天室

项目需求:

  1. 如果有用户登录,其他用户可以收到这个人的登录信息
  2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
  3. 如果有人下线,其他用户可以收到这个人的下线信息
  4. 服务器可以发送系统信息

server.c

#include <myhead.h>
struct Node
{
    char usrName[20];  //用户名
    struct sockaddr_in cin;
    struct Node *next;  //指针域
};

struct msgTyp
{
    char type;
    char usrName[20];
    char msgText[1024];
};

int main(int argc,const char *argv[])
{
    if(argc!=3)
    {
        printf("请输入IP地址和端口号\n");
        return -1;
    }

    //创建用于通信的服务器套接字文件描述符
    int sfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sfd==-1)
    {
        perror("socket error");
        return -1;
    }

    //为套接字绑定ip地址和端口号
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(atoi(argv[2]));
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))==-1)
    {
        perror("bind error");
        return -1;
    } 

    struct msgTyp buf;
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);

    recvfrom(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen);
    struct Node *L = (struct Node *)malloc(sizeof(struct Node));
    if(buf.type=='L')
    {
        strcpy(L->usrName,buf.usrName);
        L->cin = cin;
        L->next = NULL;
        printf("%s[%s:%d]登陆成功\n",buf.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
    }
    

    //创建父子进程
    pid_t pid = fork();
    if(pid<0)
    {
        perror("fork error");
        return -1;
    }
    if(pid==0)
    {
        //子进程,用于发送系统消息
        while(1)
        {
            memset(&buf,0,sizeof(buf));

            char msgText[1024]="";
            fgets(msgText,sizeof(msgText),stdin);
            msgText[strlen(msgText)-1]=0;

            buf.type = 'S';
            strcpy(buf.usrName,"**system**");
            strcpy(buf.msgText,msgText);
            //给父进程发送一个消息,在由父进程转发给用户
            if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1)
            {
                perror("发送error");
                return -1;
            }
            printf("%s[%s:%s]chat成功\n",buf.usrName,argv[1],argv[2]);
        }
        exit(EXIT_SUCCESS);
    }

    //父进程,用于收消息
    struct Node *P=L;
    while(1)
    {
        memset(&buf,0,sizeof(buf));

        recvfrom(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen);

        if(buf.type=='L')  //表示发送的是登陆信息
        {
            //遍历链表,告知其他人有人登陆了
            struct Node *Q=L;
            struct sockaddr_in sin;
            while(Q!=NULL)
            {
                sin = Q->cin;
                if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1)
                {
                    perror("发送error");
                    return -1;
                }
                Q=Q->next;
            }
            //将信息放入链表
            struct Node *F = (struct Node *)malloc(sizeof(struct Node));
            strcpy(F->usrName,buf.usrName);
            F->cin = cin;
            F->next = NULL;
            P->next = F;
            P = F;
            printf("%s[%s:%d]登陆成功\n",buf.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
            
        }
        else if(buf.type=='C'||buf.type=='S')   //表示发送的是聊天信息
        {
            //遍历链表,将聊天信息告诉链表中的所有人
            struct Node *Q=L;
            struct sockaddr_in sin;
            while(Q!=NULL)
            {
                sin = Q->cin;
                if(strcmp(Q->usrName,buf.usrName)==0)
                {
                    Q=Q->next;
                    continue;
                }
                if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1)
                {
                    perror("发送error");
                    return -1;
                }
                Q=Q->next;
            }
            if(buf.type=='C')
            {
                printf("%s[%s:%d]chat成功\n",buf.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
            }
        }
        else if(buf.type=='Q')   //表示发送的是退出信息
        {
            //遍历链表,告知其他人此用户已经退出
            struct Node *Q=L;
            struct sockaddr_in sin;
            while(Q!=NULL)
            {
                sin = Q->cin;
                if(sendto(sfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1)
                {
                    perror("发送error");
                    return -1;
                }
                Q=Q->next;
            }
            //遍历链表,删除退出用户的链表信息
            Q=L;
            if(strcmp(Q->usrName,buf.usrName)==0)
            {
                L=L->next;
                free(Q);
                Q=NULL;
            }
            else
            {
                while(strcmp(Q->next->usrName,buf.usrName)!=0)
                {
                    Q=Q->next;
                }
                struct Node *B=Q->next;
                Q->next = B->next;
                free(B);
                B=NULL;
            }
        }
    }

    close(sfd);
    return 0;
}

client.c 

#include <myhead.h>

struct msgTyp
{
    char type;
    char usrName[20];
    char msgText[1024];
};

int main(int argc,const char *argv[])
{
    if(argc!=3)
    {
        printf("请输入IP地址和端口号\n");
        return -1;
    }

    //创建用于通信的服务器套接字文件描述符
    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_port = htons(atoi(argv[2]));
    sin.sin_addr.s_addr = inet_addr(argv[1]);

    char usrname[20]="";
    printf("请输入姓名>>>");
    fgets(usrname,sizeof(usrname),stdin);
    usrname[strlen(usrname)-1]=0;

    struct msgTyp buf;
    buf.type = 'L';
    strcpy(buf.usrName,usrname);

    if(sendto(cfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1)
    {
        perror("发送error");
        return -1;
    }

    //创建父子进程
    pid_t pid = fork();
    if(pid<0)
    {
        perror("fork error");
        return -1;
    }
    if(pid==0)
    {
        //子进程,用于发送消息
        while(1)
        {
            memset(&buf,0,sizeof(buf));
            char msgText[1024]="";
            fgets(msgText,sizeof(msgText),stdin);
            msgText[strlen(msgText)-1]=0;
            if(strcmp(msgText,"quit")==0)
            {
                buf.type = 'Q';
            }
            else
            {
                buf.type = 'C';
            }
            strcpy(buf.usrName,usrname);
            strcpy(buf.msgText,msgText);
            if(sendto(cfd,&buf,sizeof(buf),0,(struct sockaddr *)&sin,sizeof(sin))==-1)
            {
                perror("发送error");
                return -1;
            }
            if(buf.type == 'Q')
            {
                break;
            }
        }
        exit(EXIT_SUCCESS);
    }

    //父进程,用于收消息
    while(1)
    {
        memset(&buf,0,sizeof(buf));
        recv(cfd,&buf,sizeof(buf),0);

        if(buf.type=='L')   //表示有其他人登陆了
        {
            printf("-----%s登录成功-----\n",buf.usrName);
        }
        else if(buf.type=='C'||buf.type=='S')   //表示有人发送了聊天信息
        {
            printf("%s:%s\n",buf.usrName,buf.msgText);
        }
        else if(buf.type=='Q')   //表示有人发送了退出信息
        {
            if(strcmp(buf.usrName,usrname)==0)
            {
                break;
            }
            printf("-----%s 已下线-----\n",buf.usrName);
        }
    }
    wait(NULL);
    close(cfd);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值