2024.3.7

文章详细描述了使用C语言编写的聊天室服务器和客户端的代码片段,包括登录、群聊、退出功能以及使用UDP套接字进行通信的过程。
摘要由CSDN通过智能技术生成

聊天室

serve.c
#include <myhead.h>
#define serve_port 8888
typedef struct
{
    char code; //操作码 'L' 登录  'C' 群聊  'Q' 退出
    char name[32];//用户名
    char txt[128];
} msg_t;
//链表结构体
typedef struct _NODE
{
    struct sockaddr_in c_addr;
    struct _NODE *next;
} node_t;
//创建链表
void creat_link(node_t **head)
{
    *head = (node_t *)malloc(sizeof(node_t));
}
//登录
int login(int sockfd, msg_t msg, struct sockaddr_in cin, node_t *phead)
{
    //将登录信息发送给所有人
    node_t *p = phead;
    while (p->next != NULL)
    {
        p = p->next;
        if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1)
        {
            printf("recvfrom error");
				return -1;
        }
    }
    //保存客户端信息
    node_t *newp = NULL;
    creat_link(&newp);
    newp->c_addr = cin;
    newp->next = phead->next;
    phead->next = newp;
    return 0;
}

int chat(int sockfd, msg_t msg, struct sockaddr_in cin, node_t *phead)
{
    //将消息发给除自己之外的人
    node_t *p = phead;
    while (p->next != NULL)
    {
        p = p->next;
        //判断信息表是否是自己,否则往后
        if (memcmp(&(p->c_addr), &cin, sizeof(cin)))
        {
            if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1)
            {
                printf("recvfrom error");
				return -1;
            }
        }
    }
    return 0;
}
//退出
int quit(int sockfd, msg_t msg, struct sockaddr_in cin, node_t *phead)
{
    node_t *p = phead;

    while (p->next != NULL)
    {
        //判断信息是否是自己,是自己就不发送并且将自己的客户端信息在链表内删除
        if (memcmp(&(p->next->c_addr), &cin, sizeof(cin)))
        {
            p = p->next;
            if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&(p->c_addr), sizeof(p->c_addr)) == -1)
            {
                printf("recvfrom error");
				return -1;
            }
        }
        else
        {
            node_t *pnew;
            pnew = p->next;
            p->next = pnew->next;
            pnew->next = NULL;
            free(pnew);
            pnew = NULL;
        }
    }
    return 0;
}
int main(int argc, const char *argv[])
{
    //对输入的参数进行判断
    if (argc != 2)
    {
        printf("argv:%s and ip\n", argv[0]);
        return -1;
    }
    //创建套接字
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("socket error");
				return -1;
    }
    //服务信息结构体
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    sin.sin_port = htons(serve_port);
    socklen_t sin_len = sizeof(sin);
    //绑定
    if (bind(sockfd, (struct sockaddr *)&sin, sin_len) == -1)
    {
        printf("bind error");
				return -1;
    }
    //客户信息结构体
    struct sockaddr_in cin;
    memset(&cin, 0, sizeof(cin));
    socklen_t cin_len = sizeof(cin);
    msg_t msg;
    //创建子进程
    pid_t pid;
    pid = fork();
    if (pid == -1)
    {
        printf("fork error");
				return -1;
    }
    else if (pid == 0)
    {
        //子进程
        //定义链表头节点
        node_t *phead = NULL;
        creat_link(&phead);
        phead->next = NULL;
        while (1)
        {
            memset(&msg, 0, sizeof(msg));
            memset(&cin, 0, sizeof(cin));
            if ((recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, &cin_len)) == -1)
            {
                printf("recvfrom error");
				return -1;
            }
            printf("%s : [%s]\n", msg.name, msg.txt);
            switch (msg.code)//根据选择进行想应的操作
            {
            case 'L':
                login(sockfd, msg, cin, phead);
                break;
            case 'C':
                chat(sockfd, msg, cin, phead);
                break;
            case 'Q':
                quit(sockfd, msg, cin, phead);
                break;
            }
        }
    }
    else if (pid > 0)
    {
        //父进程
        //发系统消息
        msg.code='C';
        strcpy(msg.name,"server");
        while(1)
        {
            fgets(msg.txt,128,stdin);
            msg.txt[strlen(msg.txt)-1]='\0';
            if(sendto(sockfd,&msg,sizeof(msg_t),0,(struct sockaddr *)&sin,sin_len)==-1)
            {
                printf("sendto error");
				return -1;
            }
        }
    }
    close(sockfd);
    return 0;
  }


client.c
#include <myhead.h>
#define CLI_port 8888
typedef struct
{
    char code; //操作码 'L' 登录  'C' 群聊  'Q' 退出
    char name[32];//用户名
    char txt[128];//发送的消息
} msg_t;

int main(int argc, const char *argv[])
{
    //入参合理性判断
    if (argc != 2)
    {
        printf("age:%s and ip \n", argv[0]);
        return -1;
    }
    //创建套接字
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        printf("socket error");
				return -1;
    }
    //创建服务器网络信息结构体
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(argv[1]);
    sin.sin_port = htons(8888);
    socklen_t sin_len = sizeof(sin);
    //给服务器发送登录数据包
    msg_t msg;
    memset(&msg, 0, sizeof(msg_t));
    msg.code = 'L';
    printf("请输入用户名:");
    fgets(msg.name, 32, stdin);
    msg.name[strlen(msg.name) - 1] = '\0';
    strcpy(msg.txt, "加入群聊");
    if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&sin, sin_len) == -1)
    {
        printf("sendto error");
				return -1;
    }
    //创建父子进程
    pid_t pid;
    pid = fork();
    if (pid == -1)
    {
        printf("fork error");
				return -1;
    }
    else if (pid == 0)
    {
        //子进程
        //接受数据并处理
        while (1)
        {
            //每次循环前将msg置零
            memset(&msg, 0, sizeof(msg));
            //接受服务器发过来的信息并打印到终端上
            if (recvfrom(sockfd, &msg, sizeof(msg_t), 0, NULL, NULL) == -1)
            {
                printf("recvfrom error");
				return -1;
            }
            printf("%s:[%s]\n", msg.name, msg.txt);
        }
    }
    else if (pid > 0)
    {
        //父进程
        //发送消息
        while (1)
        {   
            //memset会把name清除
            msg.code = 'C';
            fgets(msg.txt, 128, stdin);//从终端读入用户名
            msg.txt[strlen(msg.txt) - 1] = '\0';
            if (strcmp(msg.txt, "quit") == 0)//退出
            {
                msg.code = 'Q';
                strcpy(msg.txt, "退出群聊");
            }
            if (sendto(sockfd, &msg, sizeof(msg_t), 0, (struct sockaddr *)&sin, sin_len) == -1)
            {
                printf("sendto error");
				return -1;
            }
            if (strcmp(msg.txt, "退出群聊") == 0)
            {
                break;
            }
        }
        kill(pid,SIGKILL);
        wait(NULL);
        close(sockfd);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值