Socket server multi-process to achieve group chat

Teaching video

Client's task: send data to the server, and can receive the information received by the server

Server function: can receive the data sent by multiple clients and send the data to all clients, that is to achieve the function of group chat, and the server can also be used as a transfer station for information storage

Client code

#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;
}

Pseudo code of the server:

  • The child process reads information to buf
  • buf writes to the message queue and sends a signal to the parent process
  • When the parent process receives the signal, it goes to the message queue to get the data, traverses the client's file descriptor array, and writes circularly to the connected client.

 

Server code:

#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;
}

 

Achieve effect

Guess you like

Origin blog.csdn.net/m0_49036370/article/details/113860979