用户定义的端口号

时间:2015-11-16 05:22:24

标签: c sockets user-defined-functions

我一直在尝试修改http://beej.us/guide/bgnet/" selectserver.c"并允许用户定义的端口号并使用它代替

#define PORT "9034"   // port we're listening on

我试图让这项工作......

int PORT;
if (argc == 1)  {
    printf("\nNo argument passed. See example: ./selectserver 1234 \n\n");
    exit(-1);
} else {
    PORT = atoi(argv[1]);
    printf("PORT NO. : %d \n", PORT);
}

char* ptrPORT = (char *)&PORT;

...

这里是我修改过的代码:

/*
** selectserver.c -- a cheezy multiperson chat server
*/

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

//#define PORT "10000"   // port we're listening on

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[] )   {
    int PORT;
    fd_set master;    // master file descriptor list
    fd_set read_fds;  // temp file descriptor list for select() 
    int fdmax;    // maximum file descriptor number
    int listener;     // listening socket descriptor
    int newfd;        // newly accept()ed socket descriptor
    struct sockaddr_storage remoteaddr; // client address
    socklen_t addrlen;
    char buf[256];    // buffer for client data
    int nbytes;

    char remoteIP[INET6_ADDRSTRLEN];

    int yes=1;        // for setsockopt() SO_REUSEADDR, below
    int i, j, rv;

    struct addrinfo hints, *ai, *p;

    FD_ZERO(&master);    // clear the master and temp sets
    FD_ZERO(&read_fds);

    // get us a socket and bind it
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    if (argc == 1)  {
        printf("\nNo argument passed. See example: ./selectserver 1234 \n\n" );
        exit(-1);
    } else {
        PORT = atoi(argv[1]);
        printf("PORT NO. : %d \n", PORT);
    }

    char* ptrPORT = (char *)&PORT;

    if ((rv = getaddrinfo(NULL, ptrPORT, &hints, &ai)) != 0) {
            fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
            exit(1);
    }

    for(p = ai; p != NULL; p = p->ai_next) {
        listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (listener < 0) {
            continue;
        }

        // lose the pesky "address already in use" error message
        setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
                close(listener);
            continue;
        }

        break;
    }

    // if we got here, it means we didn't get bound
    if (p == NULL) {
        fprintf(stderr, "selectserver: failed to bind\n");
        exit(2);
    }

    freeaddrinfo(ai); // all done with this

    // listen
    if (listen(listener, 10) == -1) {
        perror("listen");
        exit(3);
    }

    // add the listener to the master set
    FD_SET(listener, &master);

    // keep track of the biggest file descriptor
    fdmax = listener; // so far, it's this one

    // main loop
    for(;;) {
        read_fds = master; // copy it
        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
            perror("select");
            exit(4);
        }

        // run through the existing connections looking for data to read
        for(i = 0; i <= fdmax; i++) {
            if (FD_ISSET(i, &read_fds)) { // we got one!!
                if (i == listener) {
                // handle new connections
                addrlen = sizeof remoteaddr;
                newfd = accept(listener,
                    (struct sockaddr *)&remoteaddr,
                    &addrlen);
                if (newfd == -1) {
                    perror("accept");
                } else {
                    FD_SET(newfd, &master); // add to master set
                    if (newfd > fdmax) {    // keep track of the max
                        fdmax = newfd;
                    }
                    printf("selectserver: new connection from %s on "
                        "socket %d\n",
                        inet_ntop(remoteaddr.ss_family,
                            get_in_addr((struct sockaddr*)&remoteaddr),
                            remoteIP, INET6_ADDRSTRLEN), newfd);
        }
            } else {
                // handle data from a client
                if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
                    // got error or connection closed by client
                    if (nbytes == 0) {
                        // connection closed
                        printf("selectserver: socket %d hung up\n", i);
                    } else {
                        perror("recv");
                    }
                    close(i); // bye!
                    FD_CLR(i, &master); // remove from master set
                } else {
                    // we got some data from a client
                    for(j = 0; j <= fdmax; j++) {
                        // send to everyone!
                        if (FD_ISSET(j, &master)) {
                            // except the listener and ourselves
                            if (j != listener && j != i) {
                                if (send(j, buf, nbytes, 0) == -1) {
                                    perror("send");
                }
                }
            }
            }
        }

                } // END handle data from client
            } // END got new incoming connection
        } // END looping through file descriptors
    } // END for(;;)--and you thought it would never end!

    return 0;
}

它会顺利编译。 现在我的问题是在执行时它会显示这个并且老实说我不知道​​如何修复它(我们昨天只进行了一次课堂讨论,所以我基本上对套接字编程没有看法)

./selectserver 10000

PORT NO. : 10000 
selectserver: nodename nor servname provided, or not known

请耐心等待我!

2 个答案:

答案 0 :(得分:1)

getaddrinfo将指向空终止字符串的指针作为第二个参数。

char* ptrPORT = (char *)&PORT;

上面的语句将指向int的指针类型转换为指向char的指针。这并没有改变它仍然指向int的事实。现在,如果仔细考虑,可以看到getaddrinfo将不知道如何正确读取您的值 - 它不是真正的空终止字符串。

但是,argv[1]是一个以空字符结尾的字符串,所以我的建议是按原样使用它而不是尝试转换它:

char* ptrPORT = argv[1];

答案 1 :(得分:0)

service参数同时接受char *和int。 但是,你真的想依靠char *和int之间的识别吗? 在手册页enter link description here上,service参数可以是一个数字作为字符串。当hints.ai_flags设置为AI_NUMERICSERV。