Различное поведение в отношении номера порта между CentOS и Windows

StackOverflow https://stackoverflow.com//questions/22059384

  •  23-12-2019
  •  | 
  •  

Вопрос

Я создал программы на языке Си, они являются сервером и клиентом.Они отправляют сообщения друг другу с помощью udp.

Сервер ожидает, пока сообщение не будет отправлено от клиента.

Когда я набираю какое-либо сообщение из клиентской консоли, клиент отправляет это сообщение на сервер.

Сервис получает сообщение от клиента, после чего сервер повторяет сообщение на своей консоли и отправляет обратно то же сообщение клиенту.

Наконец, клиент показывает на своей консоли сообщение о том, что сервер отправил ответное сообщение.

В этой процедуре клиент показывает номер исходного порта на своей консоли.А сервер также показывает номер исходного порта клиента, с которым было отправлено сообщение recvfrom ()

Как ни странно, номер исходного порта отличается у клиента и сервера, если я запускаю их на windows7, но если я запускаю их на CentOS6.4, номер исходного порта тот же.

Кто-нибудь знает, как это происходит?

Мой код следующий.

[сервер]

#define _WIN32_WINNT 0x0501
#include <stdio.h>
#include <sys/types.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
int charToInt(char myText[]) {
     char s[] = {'1', '2', '3', '4'};
     const int n = strlen(myText);
     int i, m = 0;

    for(i = 0; i < n; ++ i){
             m = m * 10 + myText[i] - '0';
    }
        printf("%d\n", m);
    return m;
 }
int
main(int argc,char *argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);

    int sock;
 struct sockaddr_in addr;
 struct sockaddr_in from;

 int sockaddr_in_size = sizeof(struct sockaddr_in);

 char buf[2048];
 char comnd[2048];
 char *bye="bye";

 printf("#############  udpServer start prot number is %d\n",charToInt(argv[1]));
 sock = socket(AF_INET, SOCK_DGRAM, 0);
 addr.sin_family = AF_INET;
 addr.sin_port = htons(charToInt(argv[1]));

 addr.sin_addr.s_addr = INADDR_ANY;
 bind(sock, (struct sockaddr *)&addr, sizeof(addr));

 while (!strncmp(buf,bye,3)==0){
        memset(buf, 0, sizeof(buf));
    recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr *)&from, &sockaddr_in_size);

    printf("recived '%s'(%d) from %s:%d\n", buf, strlen(buf),
                              inet_ntoa(from.sin_addr),ntohs(from.sin_port));

         sendto(sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, sizeof(from));

        printf("send back %s to %s:%d\n", buf,inet_ntoa(from.sin_addr),ntohs(from.sin_port));
    printf("\n");
  } 
  printf("bye now");
  close(sock);
  return 0;
}

[клиент]

 #define _WIN32_WINNT 0x0501
#include <stdio.h>
#include <sys/types.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
#include <errno.h>
int charToInt(char myText[]) {
     char s[] = {'1', '2', '3', '4'};
     const int n = strlen(myText);
     int i, m = 0;

    for(i = 0; i < n; ++ i){
             m = m * 10 + myText[i] - '0';
    }
        printf("%d\n", m);
    return m;
 }

 int getMyPortNum(int sock)
{
    struct sockaddr_in s;
    socklen_t sz = sizeof(s);
    getsockname(sock, (struct sockaddr *)&s, &sz);
    return s.sin_port;
}

int
main(int agrc,char *argv[])
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);

    char *host;
    int port;
    int sock;
 struct sockaddr_in dst_addr = {0};
 struct sockaddr_in src_addr = {0};
 struct sockaddr_in rcv_addr = {0};

 int sockaddr_in_size = sizeof(struct sockaddr_in);
 int defPortNum;


 char message[2048];
 char comnd[2048];
 int i;
 int ret;
 int connect_ret;
 int bind_ret;
 char *p;
 char buf[2048];

 host=argv[1];
 port=charToInt(argv[2]);

 printf("host = %s\n",host);
 printf("port = %d\n",port);
 sock = socket(AF_INET, SOCK_DGRAM, 0);

 dst_addr.sin_family = AF_INET;
 dst_addr.sin_addr.s_addr = inet_addr(host);
 dst_addr.sin_port = htons(port);

 printf("getMyPortNum before bind() is %d\n",ntohs(src_addr.sin_port));

 bind_ret = 0;

 bind_ret = bind(sock,(struct sockaddr *)&src_addr,sizeof(src_addr));
    src_addr.sin_port = getMyPortNum(sock);

 printf("Default Client port is %d\n",ntohs(src_addr.sin_port));
 if(bind_ret>=0){
    printf("bind() error ret = %d:%s\n",bind_ret,strerror(errno));
    perror("bind()");
    return bind_ret;
 } 

 memset(message, 0, sizeof(message));
 memset(comnd, 0, sizeof(comnd));
 memset(buf,0,sizeof(buf));

 while(!strncmp(comnd,"bye",3)==0){
    if(strncmp(message,"bye",3)==0){
        strcpy(comnd,message);
    }else{
            printf("typ your message (exit:stop Client bye:stop server)>>>\t"); 
        fgets(comnd,sizeof(comnd),stdin);
        comnd[strlen(comnd) - 1] = '\0'; 
        strcpy(message,comnd);
    }
    ret = sendto(sock, message, strlen(message), 0,
             (struct sockaddr *)&dst_addr, sizeof(dst_addr));
    printf("Server port (dst port) for sending is %d\n",ntohs(dst_addr.sin_port));
    if(ret<0){
        printf("Send Error ret = %d:%s\n",ret,strerror(errno));
        return ret;
    }else{
        printf("Waiting for sendBack !!!\n");
        printf("Client port for recieving  is %s:%d\n"
                ,inet_ntoa(src_addr.sin_addr),ntohs(src_addr.sin_port));
        ret = recvfrom(sock, buf, sizeof(buf), 
                0,(struct sockaddr *)&rcv_addr, &sockaddr_in_size); 
        if(ret<0){
            printf("ReciveError ret = %d\n",ret);
        }else{
            printf("Sentback %s from %s:%d\n"
                    ,buf,inet_ntoa(rcv_addr.sin_addr)
                    ,ntohs(rcv_addr.sin_port));
        }
    }
 }
 close(sock);
}
Это было полезно?

Решение

Вполне возможно, что при каждом вызове используется новый случайный порт источника sendto(), если только вы явно не bind() клиентский сокет к определенному исходному порту (и не полагаться на то, что ОС выполняет неявное bind() для вас).Это единственный надежный способ, которым клиент мог бы отобразить свой собственный исходный порт, поскольку sendto() не сообщает о фактически используемом исходном порту.Помните, что в отличие от TCP, UDP не требует подключения, поэтому порт источника не обязан оставаться согласованным, если вы не принудите его.

Обновить:в вашем клиентском коде есть одна строка, где он регистрирует сетевой порядок байтов номер порта, когда он должен регистрировать порядок байтов хоста номер порта вместо этого:

//printf("getMyPortNum before bind() is %d\n",myName.sin_port);
printf("getMyPortNum before bind() is %d\n",port);

Кроме того, зачем вы создали свой собственный charToInt() функция, вместо использования стандартной функции, например atoi() или strtol()?

Вы также не очень хорошо справляетесь с обработкой ошибок.

Попробуйте вместо этого что-нибудь более похожее на это:

[Сервер]

#define _WIN32_WINNT 0x0501
#include <stdio.h>
#include <sys/types.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

int printerror2(char func[], int errnum)
{
    printf("%s error = %d:%s\n", func, errnum, strerror(errnum));
    perror(func);
    return errnum;
}

int printerror(char func[])
{
    return printerror2(func, errno);
}

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (ret != 0)
        return printerror2("WSAStartup()", ret);

    int sock;
    in_port_t port;
    struct sockaddr_in addr;
    struct sockaddr_in from;
    int from_size;

    char buf[2048];

    port = atoi(argv[1]);
    printf("############# udpServer port number is %hu\n", port);

    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == -1)
        return printerror("socket()");

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
        return printerror("bind()");

    do
    {
        from_size = sizeof(from);

        ret = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&from, &from_size);
        if (ret == -1)
            return printerror("recvfrom()");

        printf("received '%*s'(%d) from %s:%hu\n",
            ret, buf, ret, inet_ntoa(from.sin_addr), ntohs(from.sin_port));

        ret = sendto(sock, buf, ret, 0, (struct sockaddr *)&from, from_size);
        if (ret == -1)
            return printerror("sendto()");

        printf("sent back '%*s'(%d) to %s:%hu\n",
            ret, buf, ret, inet_ntoa(from.sin_addr), ntohs(from.sin_port));
        printf("\n");
    } 
    while ((ret != 3) || (strncmp(buf, "bye", 3) != 0));

    printf("bye now");

    close(sock);
    return 0;
}

[Клиент]

#define _WIN32_WINNT 0x0501
#include <stdio.h>
#include <sys/types.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

int printerror2(char func[], int errnum)
{
    printf("%s error = %d:%s\n", func, errnum, strerror(errnum));
    perror(func);
    return errnum;
}

int printerror(char func[])
{
    return printerror2(func, errno);
}

int getMyPortNum(int sock, in_port_t *port)
{
    struct sockaddr_in s;
    socklen_t sz = sizeof(s);
    int ret = getsockname(sock, (struct sockaddr *)&s, &sz);
    if (ret == 0)
       *port = s.sin_port;
    return ret;
}

int main(int agrc, char *argv[])
{
    WSADATA wsaData;
    int ret = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (ret != 0)
        return printerror2("WSAStartup", ret);

    char *host;
    in_port_t port;
    int sock;
    struct sockaddr_in dst_addr;
    struct sockaddr_in src_addr;
    struct sockaddr_in from_addr;
    int from_size;

    char buf[2048];

    host = argv[1];
    port = atoi(argv[2]);

    printf("host = %s\n", host);
    printf("port = %hu\n", port);

    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == -1)
        return printerror("socket()");

    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.sin_family = AF_INET;
    src_addr.sin_addr.s_addr = INADDR_ANY;
    src_addr.sin_port = 0;

    ret = bind(sock, (struct sockaddr *)&src_addr, sizeof(src_addr));
    if (ret == -1)
        return printerror("bind()");

    ret = getMyPortNum(sock, &(src_addr.sin_port));
    if (ret == -1)
        return printerror("getsockname()");

    printf("Client port is %hu\n", ntohs(src_addr.sin_port));

    memset(&dst_addr, 0, sizeof(dst_addr));
    dst_addr.sin_family = AF_INET;
    dst_addr.sin_addr.s_addr = inet_addr(host);
    dst_addr.sin_port = htons(port);

    do
    {
        printf("type your message (exit: stop Client, bye: stop server)>>>\t"); 
        fgets(buf, sizeof(buf), stdin);

        if (strcmp(buf, "exit") == 0)
            break;

        ret = sendto(sock, buf, strlen(buf), 0, (struct sockaddr *)&dst_addr, sizeof(dst_addr));
        if (ret == -1)
            return printerror("sendto()");

        printf("Waiting for send back !!!\n");

        from_size = sizeof(from_addr);

        ret = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&from_size, &from_size); 
        if (ret == -1)
            return printerror("recvfrom()");

        printf("Received '%*s' from %s:%hu\n",
            ret, buf, inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port));
    }
    while ((ret != 3) || (strncmp(buf, "bye", 3) != 0));

    close(sock);
    return 0;
}

Другие советы

 return s.sin_port;
.

Это должно быть

return ntohs(s.sin_port);
.

Это работает в CentOS предположительно, потому что «NTOHS (I)== я там.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top