项目-网络聊天室

项目要求

利用UDP协议,实现一套聊天室软件。服务器端记录客户端的地址,客户端发送消息后,服务器群发给各个客户端软件。

问题思考

● 客户端会不会知道其它客户端地址?

UDP客户端不会直接互连,所以不会获知其它客户端地址,所有客户端地址存储在服务器端。

● 有几种消息类型?

登录:服务器存储新的客户端的地址。把某个客户端登录的消息发给其它客户端。

聊天:服务器只需要把某个客户端的聊天消息转发给所有其它客户端。

退出:服务器删除退出客户端的地址,并把退出消息发送给其它客户端。

● 服务器如何存储客户端的地址?

链表节点结构体:
struct node{
	struct sockaddr_in addr;//data   memcmp
	struct node *next;
};

消息对应的结构体(同一个协议)
typedef struct msg_t
{
    int type;//'L' C  Q    enum un{login,chat,quit};
    char name[32];//用户名
    char text[128];//消息正文
}MSG_t;

int memcmp(void *s1,void *s2,int size)

服务器端

 客户端

 

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

#include "head.h"

typedef struct node_t
{
    struct sockaddr_in addr;
    struct node_t *next;
} list_t;

list_t *createList(void);
void server_login(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr);
void server_chat(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr);
void server_quit(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr);
int main(int argc, char const *argv[])
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    socklen_t len = sizeof(caddr);

    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }
    MSG_t msg;
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.");
        return -1;
    }
    else if (pid == 0)
    {
        //创建一个空的链表
        list_t *p = createList();
        while (1)
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&caddr, &len) < 0)
            {
                perror("recvfrom err.");
                return -1;
            }
            switch (msg.type)
            {
            case login:
                server_login(sockfd, p, msg, caddr);
                break;
            case chat:
                server_chat(sockfd, p, msg, caddr);
                break;
            case quit:
                server_quit(sockfd, p, msg, caddr);
                break;
            }
        }
    }else{
        while(1)
        {
            msg.type=chat;
            strcpy(msg.name,"server");
            fgets(msg.text, sizeof(msg.text), stdin);
            if (msg.text[strlen(msg.text) - 1] == '\n')
                msg.text[strlen(msg.text) - 1] = '\0';
            sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, sizeof(saddr));
        }
    }
    close(sockfd);
    return 0;
}
//quit:
//1.将谁退出消息转发给所有登录的客户端
//2.将推出客户端信息从链表中清除
void server_quit(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    list_t *pdel = NULL;
    sprintf(msg.text, "%s quit", msg.name);
    while (p->next != NULL)
    {
        if (memcmp(&(p->next->addr), &caddr, sizeof(caddr)) == 0)
        {
            pdel = p->next;
            p->next = pdel->next;
            free(pdel);
            pdel = NULL;
        }
        else
        {
            p = p->next;
            sendto(sockfd, &msg, sizeof(msg), 0,
                   (struct sockaddr *)&(p->addr), sizeof(p->addr));
        }
    }
}
//chat:
//1.将聊天转发除自己之外的所有客户端(sockfd,p,msg,caddr)
void server_chat(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    //遍历链表转发消息
    while (p->next != NULL)
    {
        p = p->next;
        if (memcmp(&(p->addr), &caddr, sizeof(caddr)) == 0)
            continue;
        sendto(sockfd, &msg, sizeof(msg), 0,
               (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }
}
//login:
//1.将谁登录转发所有已经登录的客户端  (链表、sockfd,msg)
//2.将新登录的客户端信息新建节点插入链表 (caddr)
void server_login(int sockfd, list_t *p, MSG_t msg, struct sockaddr_in caddr)
{
    sprintf(msg.text, "%s login", msg.name);
    while (p->next != NULL)
    {
        p = p->next;
        sendto(sockfd, &msg, sizeof(msg), 0,
               (struct sockaddr *)&(p->addr), sizeof(p->addr));
    }
    //创建新节点
    list_t *pnew = (list_t *)malloc(sizeof(list_t));
    pnew->addr = caddr;
    pnew->next = NULL;

    //链接到链表最后
    p->next = pnew;
}

//创建一个空的有头单向链表
list_t *createList(void)
{
    list_t *p = (list_t *)malloc(sizeof(list_t));
    if (NULL == p)
    {
        perror("malloc err.");
        return NULL;
    }
    //初始化节点
    p->next = NULL;
    return p;
}
client.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>

#include "head.h"
int main(int argc, char const *argv[])
{
    //创建数据报
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);

    //1.登录
    MSG_t msg;
    msg.type = login;
    printf("please input your name:");
    fgets(msg.name, sizeof(msg.name), stdin);
    if (msg.name[strlen(msg.name) - 1] == '\n')
        msg.name[strlen(msg.name) - 1] = '\0';

    sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&saddr, sizeof(saddr));

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.");
        return -1;
    }
    else if (pid == 0)
    {
        while (1)
        {
            if (recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL) < 0)
            {
                perror("recvfrom err.");
                return -1;
            }
            printf("%s said %s\n", msg.name, msg.text);
        }
    }
    else
    {
        while (1)
        {
            fgets(msg.text, sizeof(msg.text), stdin);
            if (msg.text[strlen(msg.text) - 1] == '\n')
                msg.text[strlen(msg.text) - 1] = '\0';
            if (strncmp(msg.text, "quit", 4) == 0)
            {
                msg.type = quit;
                sendto(sockfd, &msg, sizeof(msg), 0,
                       (struct sockaddr *)&saddr, sizeof(saddr));
                break;
            }
            else
            {
                msg.type = chat;
            }
            sendto(sockfd, &msg, sizeof(msg), 0,
                   (struct sockaddr *)&saddr, sizeof(saddr));
        }
        kill(pid, SIGKILL);
        wait(NULL);
    }
    close(sockfd);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值