programing

UDP 접속의 양끝을 바인드() 및 connect()할 수 있습니까?

prostudy 2022. 7. 11. 21:28
반응형

UDP 접속의 양끝을 바인드() 및 connect()할 수 있습니까?

포인트 투 포인트메시지 큐 시스템을 쓰고 있는데, UDP를 통해 작동할 수 있어야 합니다.저는 임의로 어느 한쪽을 "서버"로 선택할 수 있지만, 양끝이 다른 쪽과 같은 유형의 데이터를 주고받기 때문에 그다지 옳지 않은 것 같습니다.

양쪽 끝을 bind()와 connect()로 하여 서로만 송수신 할 수 있습니까?그것은 그것을 하기 위한 훌륭한 대칭적인 방법처럼 보인다.

2018년 먼 미래에서 2012년까지 안녕하세요.

이면에는 connect()(복된 POSIX와 실장에서는 이론적으로는 필요하지 않지만) 실제로 UDP 소켓을 도입할 수 있습니다.

통상의 UDP 소켓은, 장래의 행선지에 대해서는 아무것도 모르기 때문에, 호출할 때마다 루트 룩업을 실행합니다.

「」의 경우는, 「」입니다.connect()특정 리모트 리시버의 IP 와 포토를 사용해 사전에 호출됩니다.운영체제 커널은 루트에 대한 참조를 적어 소켓에 할당할 수 있기 때문에, 그 후에 메시지를 송신하는 것이 큰폭으로 빨라집니다.sendmsg()콜은 수신자를 지정하지 않고(이전 설정은 무시됩니다), 대신 기본 설정을 선택합니다.

~의 을 확인합니다.

if (connected)
    rt = (struct rtable *)sk_dst_check(sk, 0);

if (!rt) {
    [..skip..]

    rt = ip_route_output_flow(net, fl4, sk);

    [..skip..]
}

Linux 커널 4.18 이전까지 이 기능은 대부분 IPv4 주소 패밀리에만 한정되어 있었습니다.다만, 4.18-rc4(및 Linux 커널 릴리스 4.18) 이후, IPv6 소켓에서도 완전하게 기능하고 있습니다.

사용하고 있는 OS에 따라 크게 달라지지만, 퍼포먼스에 이점이 있을 수 있습니다.적어도 Linux 를 사용하고 있고, 복수의 리모트 핸들러에 소켓을 사용하지 않는 경우는, 한번 사용해 보는 것이 좋습니다.

여기에서는, 같은 UDP 소켓상에서 특정의 송신원포트 및 행선지 포토에 각각 바인드() 및 connect() 하는 방법을 나타냅니다.이 프로그램은 모든 Linux 시스템에서 컴파일할 수 있으며 다음과 같은 용도로 사용됩니다.

usage: ./<program_name> dst-hostname dst-udpport src-udpport

나는 이 코드를 두 개의 단말기를 여는 것을 테스트했다.수신인 노드에 메시지를 발송하고 수신인 노드로부터 메시지를 수신할 수 있어야 합니다.

터미널 1 실행 중

./<program_name> 127.0.0.1 5555 5556

터미널 2 실행 중

./<program_name> 127.0.0.1 5556 5555

1대의 머신으로 테스트하고 있습니다만, 방화벽 설정을 올바르게 하면, 2대의 다른 머신에서도 동작할 수 있다고 생각합니다.

다음은 흐름에 대한 설명입니다.

  1. 설치 힌트는 대상 주소의 유형을 UDP 연결의 주소로 나타냈습니다.
  2. getaddrinfo를 사용하여 수신인 주소인 인수 1과 수신인 포트인 인수 2에 따라 주소 정보 구조 dstinfo를 가져옵니다.
  3. dstinfo의 첫 번째 유효한 엔트리를 사용하여 소켓을 만듭니다.
  4. getaddrinfo를 사용하여 주로 송신원포트의 상세 주소 정보 구조 srcinfo를 가져옵니다.
  5. srcinfo를 사용하여 얻은 소켓에 바인딩합니다.
  6. 이제 dstinfo의 첫 번째 유효한 엔트리에 연결합니다.
  7. 모든 것이 좋으면 루프로 들어갑니다.
  8. 루프는 Select를 사용하여 작성된STDIN 및 sockfd 소켓으로 구성된 읽기 기술자 목록에서 차단합니다.
  9. STDIN에 입력이 있는 경우 sendall 함수를 사용하여 수신처 UDP 연결로 전송됩니다.
  10. EOM이 수신되면 루프는 종료됩니다.
  11. sockfd에 데이터가 있는 경우 recv를 통해 읽습니다.
  12. recv가 -1을 반환하는 경우 이는 오류입니다. perror를 사용하여 디코딩하려고 합니다.
  13. recv가 0을 반환하는 경우 원격 노드가 연결을 닫았음을 의미합니다.그러나 나는 connectionless인 UDP a에는 영향이 없다고 생각한다.

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

#define STDIN 0

int sendall(int s, char *buf, int *len)
{
    int total = 0;        // how many bytes we've sent
    int bytesleft = *len; // how many we have left to send
    int n;

    while(total < *len) {
        n = send(s, buf+total, bytesleft, 0);
        fprintf(stdout,"Sendall: %s\n",buf+total);
        if (n == -1) { break; }
        total += n;
        bytesleft -= n;
    }

    *len = total; // return number actually sent here

    return n==-1?-1:0; // return -1 on failure, 0 on success
} 

int main(int argc, char *argv[])
{
   int sockfd;
   struct addrinfo hints, *dstinfo = NULL, *srcinfo = NULL, *p = NULL;
   int rv = -1, ret = -1, len = -1,  numbytes = 0;
   struct timeval tv;
   char buffer[256] = {0};
   fd_set readfds;

   // don't care about writefds and exceptfds:
   //     select(STDIN+1, &readfds, NULL, NULL, &tv);

   if (argc != 4) {
      fprintf(stderr,"usage: %s dst-hostname dst-udpport src-udpport\n");
      ret = -1;
      goto LBL_RET;
   }


   memset(&hints, 0, sizeof hints);
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_DGRAM;        //UDP communication

   /*For destination address*/
   if ((rv = getaddrinfo(argv[1], argv[2], &hints, &dstinfo)) != 0) {
      fprintf(stderr, "getaddrinfo for dest address: %s\n", gai_strerror(rv));
      ret = 1;
      goto LBL_RET;
   }

   // loop through all the results and make a socket
   for(p = dstinfo; p != NULL; p = p->ai_next) {

      if ((sockfd = socket(p->ai_family, p->ai_socktype,
                  p->ai_protocol)) == -1) {
         perror("socket");
         continue;
      }
      /*Taking first entry from getaddrinfo*/
      break;
   }

   /*Failed to get socket to all entries*/
   if (p == NULL) {
      fprintf(stderr, "%s: Failed to get socket\n");
      ret = 2;
      goto LBL_RET;
   }

   /*For source address*/
   memset(&hints, 0, sizeof hints);
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_DGRAM;        //UDP communication
   hints.ai_flags = AI_PASSIVE;     // fill in my IP for me
   /*For source address*/
   if ((rv = getaddrinfo(NULL, argv[3], &hints, &srcinfo)) != 0) {
      fprintf(stderr, "getaddrinfo for src address: %s\n", gai_strerror(rv));
      ret = 3;
      goto LBL_RET;
   }

   /*Bind this datagram socket to source address info */
   if((rv = bind(sockfd, srcinfo->ai_addr, srcinfo->ai_addrlen)) != 0) {
      fprintf(stderr, "bind: %s\n", gai_strerror(rv));
      ret = 3;
      goto LBL_RET;
   }

   /*Connect this datagram socket to destination address info */
   if((rv= connect(sockfd, p->ai_addr, p->ai_addrlen)) != 0) {
      fprintf(stderr, "connect: %s\n", gai_strerror(rv));
      ret = 3;
      goto LBL_RET;
   }

   while(1){
      FD_ZERO(&readfds);
      FD_SET(STDIN, &readfds);
      FD_SET(sockfd, &readfds);

      /*Select timeout at 10s*/
      tv.tv_sec = 10;
      tv.tv_usec = 0;
      select(sockfd + 1, &readfds, NULL, NULL, &tv);

      /*Obey your user, take his inputs*/
      if (FD_ISSET(STDIN, &readfds))
      {
         memset(buffer, 0, sizeof(buffer));
         len = 0;
         printf("A key was pressed!\n");
         if(0 >= (len = read(STDIN, buffer, sizeof(buffer))))
         {
            perror("read STDIN");
            ret = 4;
            goto LBL_RET;
         }

         fprintf(stdout, ">>%s\n", buffer);

         /*EOM\n implies user wants to exit*/
         if(!strcmp(buffer,"EOM\n")){
            printf("Received EOM closing\n");
            break;
         }

         /*Sendall will use send to transfer to bound sockfd*/
         if (sendall(sockfd, buffer, &len) == -1) {
            perror("sendall");
            fprintf(stderr,"%s: We only sent %d bytes because of the error!\n", argv[0], len);
            ret = 5;
            goto LBL_RET;
         }  
      }

      /*We've got something on our socket to read */
      if(FD_ISSET(sockfd, &readfds))
      {
         memset(buffer, 0, sizeof(buffer));
         printf("Received something!\n");
         /*recv will use receive to connected sockfd */
         numbytes = recv(sockfd, buffer, sizeof(buffer), 0);
         if(0 == numbytes){
            printf("Destination closed\n");
            break;
         }else if(-1 == numbytes){
            /*Could be an ICMP error from remote end*/
            perror("recv");
            printf("Receive error check your firewall settings\n");
            ret = 5;
            goto LBL_RET;
         }
         fprintf(stdout, "<<Number of bytes %d Message: %s\n", numbytes, buffer);
      }

      /*Heartbeat*/
      printf(".\n");
   }

   ret = 0;
LBL_RET:

   if(dstinfo)
      freeaddrinfo(dstinfo);

   if(srcinfo)
      freeaddrinfo(srcinfo);

   close(sockfd);

   return ret;
}

UDP는 접속이 없기 때문에 OS가 실제로 접속하는 것은 의미가 없습니다.

에서는 BSD를 할 수 .connect으로 UDP의 합니다.send으로 주어짐)send_to를 참조해 주세요.

UDP 소켓에 바인드하면 소켓의 종류에 관계없이 실제로 패킷을 받아들이기 위한 착신 로컬인터페이스 주소가 OS에 통지됩니다(다른 주소에 대한 모든 패킷은 폐기됩니다).

하셔야 합니다.recvfrom이치노어떤 종류의 인증을 필요로 하는 경우, 관련된 주소만 사용하는 것은 잠금이 전혀 없는 것과 마찬가지로 안전하지 않습니다.TCP 접속을 납치할 수 있으며, 나체의 UDP에는 말 그대로 IP 스푸핑이 머리 전체에 쓰여져 있습니다. 종류의 HMAC를 해야 합니다.

는 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 네!connect():

소켓 sockfd가 SOCK_DGRAM 유형인 경우 addr은 기본적으로 데이터그램이 전송되는 주소이며 데이터그램이 수신되는 유일한 주소입니다.

코드에 문제가 있습니다.

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;        //UDP communication

/*For destination address*/
if ((rv = getaddrinfo(argv[1], argv[2], &hints, &dstinfo)) 

AF_를 사용하여UNSPEC 및 SOCK_DGRAM에만 해당되며 가능한 모든 주소 목록이 나타납니다.따라서 소켓을 호출할 때 사용하는 주소가 예상한 UDP 주소가 아닐 수 있습니다.를 사용해 주세요.

hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;

취득하고 있는 addrinfo가 원하는 것인지 확인합니다.

즉, 작성한 소켓이 UDP 소켓이 아닌 경우가 있기 때문에 동작하지 않습니다.

이 페이지에는 연결된 소켓과 연결되지 않은 소켓에 대한 유용한 정보가 포함되어 있습니다.http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch08lev1sec11.html

이 인용문은 질문에 대한 답변입니다.

통상, 콜이 접속되는 것은 UDP 클라이언트입니다만, UDP 서버가 1 개의 클라이언트와 장시간 통신하는 애플리케이션(예를 들면, TFTP)이 있습니다.이 경우, 클라이언트와 서버 모두 접속을 호출할 수 있습니다.

네, 가능합니다.나도 해.

또, 이것이 도움이 되는 것은, 양쪽 모두 클라이언트와 서버로서 기능해, 양쪽에 1개의 프로세스 밖에 없는 경우입니다.

나는 UDP가 제공하는 것에 대한 생각에서 그것을 좀 더 살펴볼 것이다.UDP는 2바이트의 송수신 포트(합계 4바이트)를 추가하는8 바이트 헤더입니다이러한 포트는 버클리 소켓과 상호 작용하여 기존의 소켓인터페이스를 제공합니다.즉, 포트가 없으면 주소에 바인딩할 수 없습니다.

일반적으로 UDP 패킷을 전송할 때 수신측 포트(소스)는 사용 후 삭제되고 송신측 포트(대상)는 원격 컴퓨터의 대상 포트입니다.먼저 바인딩을 한 후 연결하면 이 기본 동작을 해제할 수 있습니다.송신원포트 및 행선지 포토는, 양쪽의 컴퓨터에 같은 포토를 사용할 수 있는 한 같습니다.

일반적으로 이 동작(포트 하이잭이라고 부릅니다)은 무시당합니다.이는 송신측 송신원포트를 동적으로 할당하는 ephemeral 모델 내에서 작업하는 것이 아니라 송신측을 1개의 프로세스에서만 송신할 수 있도록 제한했기 때문입니다.

덧붙여서 8바이트의 UDP 페이로드, 길이 및 CRC의 나머지 4바이트는 IP 패킷으로 이미 제공되고 UDP 헤더의 길이는 고정되어 있기 때문에 거의 사용되지 않습니다.다른 사람들처럼, 컴퓨터는 약간의 뺄셈을 꽤 잘합니다.

UDP에서 connect()를 사용한 적이 없습니다.connect()는 UDP와 TCP에서 전혀 다른 목적으로 설계되어 있는 것 같습니다.

man 페이지에는 UDP에서 connect()를 사용하는 방법에 대한 간단한 정보가 나와 있습니다.

일반적으로 연결 기반 프로토콜(TCP와 같은) 소켓은 한 번만 연결할 수 있습니다. 연결 없는 프로토콜(UDP와 같은) 소켓은 연결을 변경하기 위해 여러 번 connect()를 사용할 수 있습니다.

c/c++ 애호가인 경우 route_io를 사용해 보십시오.

쉽게 사용할 수 있습니다.인스턴스를 생성하여 다른 포트 루팅을 함수에 수용합니다.

예:

  void read_data(rio_request_t *req);
  void read_data(rio_request_t *req) {
  char *a = "CAUSE ERROR FREE INVALID";

  if (strncmp( (char*)req->in_buff->start, "ERROR", 5) == 0) {
    free(a);
  }
  // printf("%d,  %.*s\n", i++, (int) (req->in_buff->end - req->in_buff->start), req->in_buff->start);
  rio_write_output_buffer_l(req, req->in_buff->start, (req->in_buff->end - req->in_buff->start));
  // printf("%d,  %.*s\n", i++, (int) (req->out_buff->end - req->out_buff->start), req->out_buff->start);
}

int main(void) {

  rio_instance_t * instance = rio_create_routing_instance(24, NULL, NULL);
  rio_add_udp_fd(instance, 12345, read_data, 1024, NULL);
  rio_add_tcp_fd(instance, 3232, read_data, 64, NULL);

  rio_start(instance);

  return 0;
}

언급URL : https://stackoverflow.com/questions/9741392/can-you-bind-and-connect-both-ends-of-a-udp-connection

반응형