Linux Network Programming: UDP chatroom(Not finshed)

udpchat.h

#ifndef __UDPCHAT_H__
#define __UDPCHAT_H__

#define NAME_LENGTH 20
#define TEXT_LENGTH 128
#define SERVER_IP "192.168.8.249"
#define SERVER_PORT 8888




#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

typedef struct _protocol
{
    char type;  // L 登录 C 聊天 Q 退出
    char name[NAME_LENGTH];
    char text[TEXT_LENGTH];
} protocol_t;

#define ERR_MSG(msg) do {\
    fprintf(stderr,"line: __%d__ ",__LINE__);\
    perror(msg);\
} while (0)

//使用单向链表存储客户端的地址信息

typedef struct _node
{
    struct sockaddr_in cin;
    
    struct _node *next;

} *linklist,Node;

//创建
linklist list_create();

//尾插
int list_insert_tail(linklist L,struct sockaddr_in e);

//任意位置删
int list_delete_value(linklist L,struct sockaddr_in e);

void list_show(linklist L);

#endif


01_udp_server.c

#include "udpchat.h"
//回收僵尸进程
void handler(int sig);



int main(int argc, char const *argv[])
{
    //捕获17号信号,注册新的处理函数,用于回收僵尸进程
    __sighandler_t s = signal(SIGCHLD,handler);
    if (SIG_ERR == s)
    {
        printf("signal");
        return -1;
    }


    //1.创建宝石套接字   socket
    int sfd = socket(AF_INET,SOCK_DGRAM,0);
    if (sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }

    //服务器自身的地址信息结构体,真实的地址信息结构体根据AF_INET查找
    //man 7 ip
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERVER_PORT);
    sin.sin_addr.s_addr = inet_addr(SERVER_IP);
    //2.绑定服务器的地址信息结构体  bind
    if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("udp server bind success\n");

    char buf[128] = {0};
    ssize_t ret = 0;

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

    protocol_t protocol;

    pid_t cpid;

    //申请地址链表
    linklist L = list_create();
    if (NULL == L)
    {
        ERR_MSG("linklist create failed\n");
    }

    cpid = fork();
    if(cpid > 0)
    {
        //父进程接收客户端信息,同时转发
        // L 登录  1.遍历链表转发登录信息    2.将登录用户的地址信息结构体添加到链表中
        // C 聊天  1.遍历链表转发的聊天信息  2.不需要发给自己
        // Q 退出  1.遍历链表转发退出信息    2.删除退出的客户端节点
        while (1)
        {
            bzero(&protocol,sizeof(protocol));
            ret = recvfrom(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&cin,&addrlen);

            switch (protocol.type)
            {
                case 'L':
                    printf("%s已登录...\n",protocol.name);
                    list_insert_tail(L,cin);
                    list_show(L);
                    //将登录消息转发其他用户
                    linklist p2 = L->next;
                    while (NULL!=p2)
                    {
                        printf("进入while循环,%d\n",__LINE__);
                        printf("p2->cin.sin_port = %d\n",p2->cin.sin_port);
                        printf("cin.sin_port = %d\n",cin.sin_port);
                        if (p2->cin.sin_port!=cin.sin_port)
                        {
                            if(sendto(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&(p2->cin),sizeof(p2->cin)) < 0)
                            {
                                fprintf(stderr,"%s login msg send fail\n",protocol.name);
                                return -1;
                            }
                            printf("%s login msg send success\n",protocol.name);
                        }
                        p2=p2->next;
                    }
                    break;
                case 'C':
                    //将聊天消息转发其他用户
                    printf("开始转发聊天信息给其他用户\n");
                    linklist p3 = L->next;
                    while (NULL!=p3)
                    {
                        if (p3->cin.sin_port!=cin.sin_port)
                        {
                            if(sendto(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&(p3->cin),sizeof(p3->cin)) < 0)
                            {
                                fprintf(stderr,"%s msg send fail\n",protocol.name);
                                return -1;
                            }
                            printf("%s msg send success\n",protocol.name);
                        }
                        p3=p3->next;
                    }
                    break;
                case 'Q':
                    printf("%s已退出...\n",protocol.name);
                    //将退出消息转发其他用户
                   linklist p4 = L->next;
                    while (NULL!=p4)
                    {
                        if (p4->cin.sin_port!=cin.sin_port)
                        {
                            if(sendto(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&(p4->cin),sizeof(p4->cin)) < 0)
                            {
                                fprintf(stderr,"%s msg send fail\n",protocol.name);
                                return -1;
                            }
                            printf("%s msg send success\n",protocol.name);
                        } else
                        {
                            //删除节点
                            list_delete_value(L,cin);
                        }
                        p4=p4->next;
                    }
                    break;
                
                default:
                    break;
            }
        }

    } else if(cpid == 0)
    {
        //子进程发送系统信息
        while (1)
        {
            bzero(&protocol,sizeof(protocol));
            strcpy(protocol.name,"administrator");
            protocol.type = 'C';
            fgets(protocol.text,TEXT_LENGTH,stdin);
            protocol.text[strlen(protocol.text)-1] = 0;

            linklist p1 = L->next;
            while (NULL!=p1)
            {
                
                if(sendto(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&(p1->cin),sizeof(p1->cin)) < 0)
                {
                    fprintf(stderr,"%s system msg send fail\n",protocol.name);
                    return -1;
                }
                printf("%s system msg send success\n",protocol.name);
        
                p1=p1->next;
            }
        }
    } else
    {
        ERR_MSG("fork");
        return -1;
    }

    //5.关闭套接字文件描述符
    close(sfd);
    return 0;
}


linklist list_create()
{
    linklist L = (linklist)malloc(sizeof(Node));
    if (NULL == L)
    {
        ERR_MSG("malloc");
        return NULL;
    }
    memset(&(L->cin),0,sizeof(L->cin));
    L->next = NULL;
    printf("链表初始化成功\n");
    return L;
}

int list_insert_tail(linklist L,struct sockaddr_in e)
{
    if (NULL == L)
    {
        ERR_MSG("insert fail");
        return -1;
    }
    //遍历到链表尾部
    linklist p = L;
    while (NULL != p->next)
    {
        p = p->next;
    }
    //申请节点
    linklist q = (linklist)malloc(sizeof(Node));
    if (NULL == q)
    {
        ERR_MSG("node apply failed\n");
        return -1;
    }
    q->next = NULL;
    q->cin = e;
    //进行尾插
    q->next = p->next;
    p->next = q;
    printf("tail insert success\n");
    return 0;
}

int list_delete_value(linklist L,struct sockaddr_in e)
{
    if (NULL == L)
    {
        ERR_MSG("delete fail");
        return -1;
    }
    linklist p = L->next;
    //找到p的前驱节点
    while (NULL!=p->next)
    {
        if ((e.sin_addr.s_addr == p->next->cin.sin_addr.s_addr)&&(e.sin_port == p->next->cin.sin_port))
        {
            break;
        }
        p = p->next;
    }
    if (NULL==p->next)
    {
        printf("delete node failed\n");
        return -1;
    } 
    else
    {
        linklist q = p->next;
        p->next = q->next;
        free(q);
        printf("delete node success\n");
        return 0;
    }
    
}

void list_show(linklist L)
{
    printf("地址链表中数据为:\n");
    linklist p = L->next;
    while (p!=NULL)
    {
         printf("[%s | %d]\n",inet_ntoa(p->cin.sin_addr),ntohs(p->cin.sin_port));
        p = p->next;
    }
    
}

void handler(int sig)
{
    while (waitpid(-1,NULL,WNOHANG)>0); //循环回收僵尸进程
}

02_udp_client.c

#include "udpchat.h"

//回收僵尸进程
void handler(int sig);

int main(int argc, char const *argv[])
{
    //捕获17号信号,注册新的处理函数,用于回收僵尸进程
    __sighandler_t s = signal(SIGCHLD,handler);
    if (SIG_ERR == s)
    {
        printf("signal");
        return -1;
    }

    //1.创建报式套接字   socket
    int sfd = socket(AF_INET,SOCK_DGRAM,0);
    if (sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }

    //服务器自身的地址信息结构体,真实的地址信息结构体根据AF_INET查找
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SERVER_PORT);
    sin.sin_addr.s_addr = inet_addr(SERVER_IP);
    socklen_t sin_addrlen = sizeof(sin);


    char buf[128] = {0};
    ssize_t ret = 0;

    struct sockaddr_in cin; //存储发送方的地址信息
    socklen_t addrlen = sizeof(cin);
    pid_t cpid;

    protocol_t protocol;
    memset(&protocol,0,sizeof(protocol));

    //进行登录操作,输入用户名
    printf("请输入用户名>>");
    fgets(protocol.name,NAME_LENGTH,stdin);
    protocol.name[strlen(protocol.name)-1] = 0;
    //构造发送消息结构体发送给服务器
    protocol.type = 'L';
    //发送登录信息
    if(sendto(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&sin,sizeof(sin)) < 0)
    {
        ERR_MSG("sendto");
        return -1;
    }
    printf("client send login info to server success\n");

    //登录成功后创建父子进程进行: 1.发送聊天信息,退出信息  2.
    cpid = fork();
    if(cpid > 0)
    {
        while (1)
        {
            bzero(protocol.text,TEXT_LENGTH);
            //父进程用于发送
            //1.聊天信息
            //2.退出==> 输入quit 退出
            fgets(protocol.text,TEXT_LENGTH,stdin);
            protocol.text[strlen(protocol.text)-1] = 0;
            if (0 == strcmp(protocol.text,"quit") )
            {
                protocol.type = 'Q';
                if(sendto(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&sin,sizeof(sin)) < 0)
                {
                    ERR_MSG("quit msg sent to server");
                    return -1;
                }
                printf("quit msg sent to server success\n");
                //结束子进程
                kill(cpid,SIGKILL);
                break;
            } else
            {
                protocol.type = 'C';
            }
            //发送
            if(sendto(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&sin,sizeof(sin)) < 0)
            {
                ERR_MSG("sendto");
                return -1;
            }
            printf("chatting msg sent to server success\n");
        }
        printf("client parent process exited\n");

    } else if(cpid == 0)
    {
        while (1)
        {
            //子进程用于接收服务器发来的数据
            bzero(&protocol,sizeof(protocol));
            ret = recvfrom(sfd,&protocol,sizeof(protocol),0,(struct sockaddr *)&sin,&sin_addrlen);
            if (ret < 0)
            {
                ERR_MSG("recvfrom");
                return -1;
            } 
            //打印接收到的信息
            switch (protocol.type)
            {
                case 'L':
                    printf("%s已登录...\n",protocol.name);
                    break;
                case 'C':
                    printf("%s:%s\n",protocol.name,protocol.text);
                    break;
                case 'Q':
                    printf("%s已退出...\n",protocol.name);
                    break;
                
                default:
                    break;
            }
        }
    } else
    {
        ERR_MSG("fork");
        return -1;
    }
    //5.关闭套接字文件描述符
    close(sfd);

    return 0;
}


void handler(int sig)
{
    while (waitpid(-1,NULL,WNOHANG)>0); //循环回收僵尸进程
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值