UDP网络聊天室

项目需求:

1.如果有用户登录,其他用户可以收到这个人的登录信息

2.如果有人发送信息,其他用户可以收到这个人的群聊信息

3.如果有人下线,其他用户可以收到这个人的下线信息

4.服务器可以发送系统信息

客户端代码

#include<myhead.h>
//用户链表结构体
typedef struct Node
{
    char usrName[20];//用户名
    struct sockaddr_in cin;//客户端地址
    struct Node *next;//指针
}Node,*NodePtr;
//消息结构体
typedef struct msgTyp
{
    char type;//消息类型
    char usrName[20];//用户名
    char msgText[1024];//消息内容
}msgTyp;
//创建头结点
NodePtr create_Node(struct sockaddr_in sin)
{
    NodePtr L=(NodePtr)malloc(sizeof(Node));
    if(NULL==L)
    {
        printf("创建失败\n");
        return NULL;
    }
    strcpy(L->usrName,"**system**");//服务器名字
    L->cin=sin;//服务器地址
    L->next=NULL;
    return L;
}
//申请结点
NodePtr apply_Node()
{
    NodePtr p=(NodePtr)malloc(sizeof(Node));
    if(NULL==p)
    {
        printf("创建失败\n");
        return NULL;
    }
    struct sockaddr_in cin;
    socklen_t addrlen=sizeof(cin);
    memset(&cin,0,addrlen);
    strcpy(p->usrName,"");
    p->cin=cin;
    p->next=NULL;
    return p;
}
//判空
int list_empty(NodePtr L)
{
    return L->next==NULL;
}
//尾插
int inser_tail(NodePtr L,struct sockaddr_in cin,msgTyp M)
{
    if(NULL==L)
    {
        printf("链表不合法\n");
        return -1;
    }
    NodePtr p=apply_Node();
    strcpy(p->usrName,M.usrName);
    p->cin=cin;
    NodePtr t=L;
    while(t->next!=NULL)
    {
        t=t->next;
    }
    t->next=p;
    return 0;
}
//查找结点位置
int search_Node(NodePtr L,struct sockaddr_in cin)
{
    if(NULL==L||list_empty(L))
    {
        printf("查找失败\n");
        return -1;
    }
    NodePtr q=L->next;
    int i=1;
    //判断端口和ip地址
    while(q->cin.sin_addr.s_addr!=cin.sin_addr.s_addr||q->cin.sin_port!=cin.sin_port)
    {
        i++;
        q=q->next;
    }
    return i;//返回位置
}
//按位置删除结点
int delete_Node(NodePtr L,struct sockaddr_in cin)
{
    if(NULL==L||list_empty(L))
    {
        printf("删除失败\n");
        return -1;
    }
    int i=search_Node(L,cin);//要删除的结点位置
    NodePtr p=L;
    int j=1;
    //找到要删除结点
    while(j<i)
    {
        p=p->next;
        j++;
    }
    NodePtr q=p->next;
    p->next=q->next;
    free(q);
    q=NULL;
    return 0;
}
//头删
int delete_head(NodePtr L)
{
    if(NULL==L||list_empty(L))
    {
        printf("删除失败\n");
        return -1;
    }
    NodePtr p=L->next;
    L->next=p->next;
    free(p);
    p=NULL;
    return 0;
}
//销毁链表
int destroy(NodePtr L)
{
    if(NULL==L)
    {
        printf("销毁失败\n");
        return -1;
    }
    while(!list_empty(L))
    {
        delete_head(L);
    }
    free(L);
    L=NULL;
    return 0;
}

int main(int argc, char const *argv[])
{
    //判断输入个数
    if(argc!=3)
    {
        printf("input error\n");
        return -1;
    }
    //创建服务器套接字
    int sfd=socket(AF_INET,SOCK_DGRAM,0);
    if(sfd==-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]);
    //绑定
    if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
    {
        perror("bind error");
        return -1;
    }
    //创建头结点
    NodePtr L=create_Node(sin);
    if(NULL==L)
    {
        return -1;
    }
    //客户端
    struct sockaddr_in cin;
    socklen_t addrlen=sizeof(cin);
    //创建子进程
    pid_t pid=fork();
    //子进程工作内容
    if(pid==0)
    {
        msgTyp msg;//定义消息变量
        char buf[1044]="";//存放数据
        while(1)
        {
            //初始化
            memset(&msg,0,sizeof(msg));
            bzero(buf,sizeof(buf));
            //接收客户端消息
            recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&addrlen);
            //判断消息类型
            switch(msg.type)
            {
                //d类型消息登录
                case 'd':
                {
                    //服务器输出登录成功
                    printf("%s [%s:%d]登录成功\n",msg.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
                    //登录成功消息存入buf
                    sprintf(buf,"-----%s登录成功-----",msg.usrName);
                    //遍历链表发送
                    NodePtr p=L->next;
                    while(p!=NULL)
                    {
                        sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&p->cin,sizeof(p->cin));
                        p=p->next;
                    }
                    //插入链表
                    inser_tail(L,cin,msg);
                }
                break;
                //t类型消息发送消息
                case 't':
                {
                    //判断端口和地址是否为服务器,服务器输出
                    if(L->cin.sin_port!=cin.sin_port||L->cin.sin_addr.s_addr!=cin.sin_addr.s_addr)
                    {
                        printf("%s [%s:%d]%s\n",msg.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),msg.msgText);
                    }
                    //消息存入buf   
                    sprintf(buf,"%s:%s",msg.usrName,msg.msgText);  
                    //遍历链表
                    NodePtr p=L->next;
                    while(p!=NULL)
                    {
                        //判断端口和地址是否和发送过来的客户端一样,不是则发送消息
                        if(p->cin.sin_port!=cin.sin_port||p->cin.sin_addr.s_addr!=cin.sin_addr.s_addr)
                        {
                            sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&p->cin,sizeof(p->cin));
                        }
                        p=p->next;
                    }
                }
                break;
                //q类型消息退出
                case 'q':
                {
                    //服务器输出
                    printf("%s [%s:%d]退出成功\n",msg.usrName,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
                    //已下线消息存入buf
                    sprintf(buf,"-----%s已下线-----",msg.usrName);
                    delete_Node(L,cin);//删除该结点
                    //遍历链表发送消息
                    NodePtr p=L->next;
                    while(p!=NULL)
                    {
                        sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&p->cin,sizeof(p->cin));
                        p=p->next;
                    }
                }
                break;
                //消息类型错误
                default:
                printf("接收消息类型错误\n");
            }
        }
    }
    //父进程工作内容
    if(pid>0)
    {
        msgTyp msg;//定义消息类型变量
        while(1)
        {
            memset(&msg,0,sizeof(msg));//初始化
            fgets(msg.msgText,sizeof(msg.msgText),stdin);//服务器输入内容存入消息内容
            msg.msgText[strlen(msg.msgText)-1]=0;
            //判断是否退出
            if(strcmp(msg.msgText,"quit")==0)
            {
                break;
            }
            msg.type='t';//消息类型为t
            strcpy(msg.usrName,"**system**");//服务器名字
            //发送给子进程服务器
            sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
        }
    }
    destroy(L);//销毁链表
    kill(pid,SIGKILL);//杀死子进程
    wait(NULL);//回收
    close(sfd);//关闭套接字
    return 0;
}

客户端代码

#include<myhead.h>
//消息结构体
typedef struct msgTyp
{
    char type;
    char usrName[20];
    char msgText[1024];
}msgTyp;
int main(int argc, char const *argv[])
{
    //判断输入个数
    if(argc!=3)
    {
        printf("input error\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]);
    socklen_t sin_addrlen=sizeof(sin);
    msgTyp msg;//定义消息变量
    //存入姓名
    printf("请输入姓名:");
    scanf("%s",msg.usrName);
    getchar();
    strcpy(msg.msgText,"");//初始化消息内容
    msg.type='d';//d类型消息
    //发送给服务器
    sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_addrlen);
    //创建子进程
    pid_t pid=fork();
    //子进程工作内容
    if(pid==0)
    {
        char buf[1024]="";//存消息
        while(1)
        {
            bzero(buf,sizeof(buf));//初始化
            //接收消息
            recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&sin_addrlen);
            printf("%s\n",buf);//输出
        }
    }
    //父进程工作内容
    if(pid>0)
    {
        while(1)
        {
            bzero(msg.msgText,sizeof(msg.msgText));//初始化消息内容
            fgets(msg.msgText,sizeof(msg.msgText),stdin);//输入消息存入
            msg.msgText[strlen(msg.msgText)-1]=0;
            //判断是否退出
            if(strcmp(msg.msgText,"quit")==0)
            {
                msg.type='q';//q类型消息
                //发送消息给服务器
                sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
                break;
            }
            msg.type='t';//t类型消息
            sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
        }
    }
    kill(pid,SIGKILL);//杀死子进程
    wait(NULL);//回收
    close(cfd);//关闭套接字
    return 0;
}

运行结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值