简体   繁体   English

在 C/C++ 中与 Openssl epoll 服务器同时与多个客户端通信

[英]Communication with Openssl epoll server to multiple clients simultaneously in C/C++

I have an SSL server (code listed below) connecting to multiple SSL client.我有一个 SSL 服务器(下面列出的代码)连接到多个 SSL 客户端。 I am using a single context and the initialization is我使用的是单个上下文,初始化是

SSL_CTX *ctx;
SSL *ssl[MAXEVENTS];
SSL_library_init();
ctx = InitServerCTX();        // initialize SSL
...
...

Then I have the following piece of code然后我有以下一段代码

ssl[i] = SSL_new(ctx);           // get new SSL state with context
SSL_set_fd(ssl[i], infd);        // set connection socket to SSL state

Then I perform SSL_accept(ssl[i]) .然后我执行SSL_accept(ssl[i])

All this is being performed using epoll (edge trigger mode).所有这些都是使用 epoll(边沿触发模式)执行的。 I have modified the example in https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/ to use SSL by referring tohttps://www.cs.utah.edu/~swalton/listings/articles/ssl_server.c as a reference我已经修改了https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/ 中的示例,参考https://www.cs使用 SSL。 utah.edu/~swalton/listings/articles/ssl_server.c作为参考

The logic for this is这样做的逻辑是

 events = new epoll_event[MAXEVENTS * sizeof event];

 // The event loop
 while (true)
 {
    int n, i;

    n = epoll_wait (efd, events, MAXEVENTS, -1);
    for (i = 0; i < n; i++)
    {
        if ((events[i].events & EPOLLERR) ||
                (events[i].events & EPOLLHUP) ||
                (!(events[i].events & EPOLLIN)))
        {
            // An error has occured on this fd, or the socket is not
            // ready for reading (why were we notified then?)
            fprintf (stderr, "epoll error\n");
            close (events[i].data.fd);
            continue;
        } else if (sfd == events[i].data.fd) {
            // We have a notification on the listening socket, which
            // means one or more incoming connections.
            while (1)
            {

                struct sockaddr in_addr;
                socklen_t in_len;
                int infd;
                char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

                in_len = sizeof in_addr;
                infd = accept (sfd, &in_addr, &in_len);
                if (infd == -1)
                {
                    if ((errno == EAGAIN) ||(errno == EWOULDBLOCK)) {
                        // We have processed all incoming
                        // connections.
                        break;
                    } else {
                        perror ("accept");
                        break;
                    }
                }

                s = getnameinfo (&in_addr, in_len,
                                 hbuf, sizeof hbuf,
                                 sbuf, sizeof sbuf,
                                 NI_NUMERICHOST | NI_NUMERICSERV);
                if (s == 0) {
                    printf("Accepted connection on descriptor %d "
                           "(host=%s, port=%s)\n", infd, hbuf, sbuf);
                }


    ssl[i] = SSL_new(ctx);           // get new SSL state with context
    SSL_set_fd(ssl[i], infd);        // set connection socket to SSL state

    int ret;
    if ( (ret=SSL_accept(ssl[i])) == FAIL ) {    // do SSL-protocol accept
    ERR_print_errors_fp(stderr);
    printf("Performing exchange Error 1.\n");
    int error = SSL_get_error(ssl[i], 0);

    //TODO A retry timer or retry counter. Cannot keep retrying perpetually.
    if (ret <=0 && (error == SSL_ERROR_WANT_READ)) {
        //Need to wait until socket is readable. Take action?
        //LOG the reason here
        perror ("Need to wait until socket is readable.");
    } else if (ret <=0 && (error == SSL_ERROR_WANT_WRITE)) {
        //Need to wait until socket is writable. Take action?
        //LOG the reason here
        perror ("Need to wait until socket is writable.");
    } else {
        //LOG the reason here
        perror ("Need to wait until socket is ready.");
    }
    shutdown (infd, 2);
    SSL_free (ssl[i]);
    continue;
    }
    // Make the incoming socket non-blocking and add it to the
    // list of fds to monitor.
    s = SocketNonBlocking (infd);
    if (s == -1) {
    abort ();
    }
    event.data.fd = infd;
    event.events = EPOLLIN | EPOLLET | EPOLLHUP;
    s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);
    if (s == -1) {
    perror ("epoll_ctl");
    abort ();
    }
}
continue;

Now,现在,

    while (1)
    {
        ssize_t count;
        char buf[1024];
        char reply[1024];

        printf("Performing exchange.\n");
        const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";

        ShowCerts(ssl[i]);        // get any certificates
        count = SSL_read(ssl[i], buf, sizeof(buf)); // get request
        int32_t ssl_error = SSL_get_error (ssl[i], count);
        switch (ssl_error) {
         case SSL_ERROR_NONE: 
                    printf("SSL_ERROR_NONE\n");
                    break;
         case SSL_ERROR_WANT_READ:
                    printf("SSL_ERROR_WANT_READ\n");
                    break;
         case SSL_ERROR_WANT_WRITE:
                    printf("SSL_ERROR_WANT_WRITE\n");
                    break;
         case SSL_ERROR_ZERO_RETURN:
                    printf("SSL_ERROR_ZERO_RETURN\n");  
                    break;
         default:
                    break;
         }

        if (( count > 0 ) )
        {
            buf[count] = 0;
            printf("count > 0 Client msg: \"%s\"\n", buf);
            sprintf(reply, HTMLecho, buf);   // construct reply
            SSL_write(ssl[i], reply, strlen(reply)); // send reply
        } else if ((count < 0)  ){
            printf("count < 0 \n");
            if (errno != EAGAIN)
            {
                printf("count < 0 errno != EAGAIN \n");
                perror ("read");
                done = 1;
            }
            break;
        } else if (count==0){
            ERR_print_errors_fp(stderr);
            epoll_ctl(efd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
            printf("count == 0 Client Disconnected.\n");
            done = 1;
            break;
        }

    }
    if (done)
    {
        printf("Freeing data.\n");
        int sd = SSL_get_fd(ssl[i]);
        SSL_free(ssl[i]);         // release SSL state
        close(sd);          // close connection
        //close (events[i].data.fd);
    }
   }

This works fine for one server - one client.这适用于一台服务器 - 一个客户端。 But the moment I try to connect two clients, the client that connected last is the only one that receives the data.但是当我尝试连接两个客户端时,最后连接的客户端是唯一接收数据的客户端。 The client that was connected before just keeps hanging without any activity.之前连接的客户端只是一直挂着,没有任何活动。

UPDATE I found that there is some indexing issue going on here.更新我发现这里存在一些索引问题。 The value of the variable i from the epoll example is not corresponding to what I think it should be corresponding. epoll 示例中的变量i的值与我认为应该对应的值不对应。 I tried connecting two clients and I had thought initially the i should have incremented for second client but it is not the case.我尝试连接两个客户端,我最初认为i应该为第二个客户端增加,但事实并非如此。 It still stays 0 .它仍然保持0

Ok I solved the issue.好的,我解决了这个问题。 My problem stemmed from incorrect indexing.我的问题源于不正确的索引。 I was relying on the variable i that did not behave the way I expected.我依赖的变量i没有按照我预期的方式运行。 (see update in my question) (请参阅我的问题中的更新)

First I declare std::map<int,SSL*> sslPairMap;首先我声明std::map<int,SSL*> sslPairMap;

Then I insert the successful fd and SSL accept into a std::map in C++.然后我将成功的 fd 和 SSL 接受插入到 C++ 中的 std::map 中。 In C, one can use struct based pairing.在 C 中,可以使用基于结构的配对。 There is one example here https://github.com/dCache/dcap/blob/b432bd322f0c1cf3e5c6a561845899eec3acad1e/plugins/ssl/sslTunnel.c这里有一个例子https://github.com/dCache/dcap/blob/b432bd322f0c1cf3e5c6a561845899eec3acad1e/plugins/ssl/sslTunnel.c

                //(c) 2014 enthusiasticgeek for stack overflow

                sslPairMap.insert(std::pair<int,SSL*>(infd, ssl));

                // Make the incoming socket non-blocking and add it to the
                // list of fds to monitor.
                s = AibSocketNonBlocking (infd);
                if (s == -1) {
                    abort ();
                }
                aibevent.data.fd = infd;
                aibevent.events = EPOLLIN | EPOLLET | EPOLLHUP;
                s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &aibevent);
                if (s == -1) {
                    perror ("epoll_ctl");
                    abort ();
                }

After this I simply retrieve the SSL* from the map which ensures I do not change index inadvertently.在此之后,我只需从地图中检索 SSL*,以确保我不会无意中更改索引。 std::map saves the day std::map 拯救了这一天

           //(c) 2014 enthusiasticgeek for stack overflow
           while (1)
            {
                ssize_t count;
                char buf[1024];
                char reply[1024];

                printf("Performing exchange where i = %d.\n",i);
                const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";

                ShowCerts(sslPairMap[aibevents[i].data.fd]);        // get any certificate

                count = SSL_read(sslPairMap[aibevents[i].data.fd], buf, sizeof(buf)); // get request
                ssl_error = SSL_get_error (sslPairMap[aibevents[i].data.fd], count);
                switch (ssl_error) {
                case SSL_ERROR_NONE: 
                            printf("SSL_ERROR_NONE\n");
                            break;
                case SSL_ERROR_WANT_READ:
                            printf("SSL_ERROR_WANT_READ\n");
                            break;
                case SSL_ERROR_WANT_WRITE:
                            printf("SSL_ERROR_WANT_WRITE\n");
                            break;
                case SSL_ERROR_ZERO_RETURN:
                            printf("SSL_ERROR_ZERO_RETURN\n");  
                            break;
                default:
                            break;
                 }

                if (( count > 0 ) )
                {
                    buf[count] = 0;
                    printf("count > 0 Client msg: \"%s\"\n", buf);
                    sprintf(reply, HTMLecho, buf);   // construct reply
                    SSL_write(sslPairMap[aibevents[i].data.fd], reply, strlen(reply)); // send reply
                    break;
                } else if ((count < 0)  ){
                    printf("count < 0 \n");
                    if (errno != EAGAIN)
                    {
                        printf("count < 0 errno != EAGAIN \n");
                        perror ("read");
                        done = 1;
                    }
                    break;
                } else if (count==0){
                    ERR_print_errors_fp(stderr);
                    epoll_ctl(efd, EPOLL_CTL_DEL, aibevents[i].data.fd, NULL);
                    printf("count == 0 Client Disconnected.\n");
                    done = 1;
                    break;
                }

            }
            if (done)
            {
                printf("Freeing data.\n");
                int sd = SSL_get_fd(sslPairMap[aibevents[i].data.fd]);
                if(ssl_error == SSL_ERROR_NONE){
                   SSL_shutdown(sslPairMap[aibevents[i].data.fd]);
                }
                SSL_free(sslPairMap[aibevents[i].data.fd]);         // release SSL state
                close(sd);          // close connection
                //close (aibevents[i].data.fd);
                erase_from_map(sslPairMap, aibevents[i].data.fd);
            }
        }

If anyone comes across this, another way to go about it is to store the SSL* pointer in the event data itself:如果有人遇到这种情况,另一种方法是将SSL*指针存储在事件数据本身中:

events[i].data.u64 = (long long)ssl;

And when you need to read/write from it:当您需要从中读取/写入时:

auto ssl = (SSL*)events[i].data.u64;
SSL_read(ssl, someBuffer, sizeof(someBuffer));

For example.例如。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM