socket服务器多进程实现群聊

教学视频

客户端的任务:发送数据给服务器,并可以接收服务器的接收到的信息

服务器功能:可以接收多个客户端的发送的数据,并将数据发送给所有的客户端,也就是达到群聊的功能,同时也可以将服务器作为一个信息储存的中转站

客户端的代码

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
using namespace std;

#include <errno.h>

#define PUB_MSG_TYPE 256

#define ERR_EXIT(m) \
    do{\
        perror(m);\
        _exit(1); \
    } while (0)


int main()
{
    int ret = 0;
    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//创建socket
    if (socket_fd == 1)
        ERR_EXIT("socket");
    //绑定ip和端口
    struct  sockaddr_in ser_addr;
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(8097);
    inet_aton("192.168.1.9", &ser_addr.sin_addr);

    ret = connect(socket_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    if (ret == -1)
        ERR_EXIT("connect");
    cout << "connect success" << endl;

    pid_t  pid = fork();
    if (pid == 0)
    {
        //子进程进行读取数据
        char rbuf[1024] = { 0 };
        while (1)
        {
            ret = read(socket_fd, rbuf, sizeof(rbuf));
            if (ret == 0)
            {
                cout << "server is off" << endl;
                break;
            }
            cout << "read:" << rbuf << endl;
            memset(rbuf, 0x0, sizeof(rbuf));
        }
        exit(EXIT_SUCCESS);
    }

    //父进程负责从键盘获取数据输入  发送给服务器
    char sbuf[1024] = { 0 };
    while (fgets(sbuf, sizeof(sbuf), stdin) != 0)//stdin标准输入
    {
        write(socket_fd, sbuf, sizeof(sbuf));
        memset(sbuf, 0x0, sizeof(sbuf));
    }
    return 0;
}

服务器的伪代码:

  • 子进程读取信息到buf
  • buf写入消息队列,并发送信号给父进程
  • 父进程收到信号,去消息队列获取数据,遍历客户端的文件描述符数组,循环write写给已连接的客户端

服务器代码:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <string.h>
#include <vector>

#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
using namespace std;

#include <errno.h>

#define PUB_MSG_TYPE 256

#define ERR_EXIT(m) \
    do{\
        perror(m);\
        exit(1); \
    } while (0)

vector<int>  vfds;//存放所有的客户端的文件描述符
int msgid;//消息队列的id

//消息结构体
typedef struct msg_buf {
    long mtype;// message type, must be > 0
    char mtest[PUB_MSG_TYPE];//message data
    //void* mtest;
}MSG_T;
#define CHAT_MSG_TYPE 10

MSG_T rcvMsg;
vector<int>::iterator  it;
//信号动作
void handler(int num)
{
    cout << "num = " << num << endl;
    //1.从消息对列中取出消息
    msgrcv(msgid, &rcvMsg, sizeof(MSG_T), CHAT_MSG_TYPE, 0);
    cout << "recv:" << rcvMsg.mtest << endl;
    //2.群发
    for (it = vfds.begin();it != vfds.end();it++)
    {
        write(*it, rcvMsg.mtest, sizeof(rcvMsg.mtest));
    }
}
//服务器
int main()
{
    int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//创建一个TCPsocket
    if (socket_fd == 1)
        ERR_EXIT("socket");
    //绑定ip和端口
    struct  sockaddr_in ser_add;
    ser_add.sin_family = AF_INET;
    ser_add.sin_port = htons(8097);//端口号
    ser_add.sin_addr.s_addr = htonl(INADDR_ANY);

    //地址可重复使用
    int on = 1;
    setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    int ret = bind(socket_fd, (struct sockaddr*)&ser_add, sizeof(ser_add));
    if (ret == -1)
        ERR_EXIT("bind");

    ret = listen(socket_fd, SOMAXCONN);//监听数据存放队列中
    if (ret == -1)
        ERR_EXIT("listen");

    struct sockaddr_in cli_add;
    socklen_t add_len = sizeof(cli_add);
    //int add_len = sizeof(cli_add);
    cout << "wait for client..." << endl;

    //安装信号
    msgid = msgget(5678, 0666 | IPC_CREAT);
    signal(SIGUSR2, handler);
    while (1)
    {
        int  con_fd = accept(socket_fd, (struct sockaddr*)&cli_add, &add_len);
        vfds.push_back(con_fd);//存放连接的客户端的文件描述符
        cout << "someone on line..." << endl;
        cout << "father  pid =  " << getpid() << endl;
        pid_t pid = fork();
        if (pid == 0)
        {
            char buf[1024] = { 0 };
            while (1)
            {
                ret = read(con_fd, buf, sizeof(buf));
                if (ret == 0)
                {
                    cout << "client is offline" << endl;
                    break;
                }
                cout << "buf=" << buf << endl;
                //1.把消息写入消息队列
                MSG_T msg;
                msg.mtype = CHAT_MSG_TYPE;
                strcpy(msg.mtest, buf);
                msgsnd(msgid, &msg, sizeof(MSG_T),0);//0非阻塞
                //2.发信号通知父进程去取数据 
                kill(getppid(), SIGUSR2);
                //write(con_fd, buf, sizeof(buf));
                memset(buf, 0, sizeof(buf));
            }
            exit(EXIT_SUCCESS);
        }
    } 


    return 0;
}

实现效果

猜你喜欢

转载自blog.csdn.net/m0_49036370/article/details/113860979