📓 Archive

  • Pricing
  • Chess
  • Syntax
  • NETWORK

    FGJ: Create:2022/10/19 Update: (2024-10-24)

    • TCP #

    • UDP #

    • InetAddress #

      • 流程解析 #

        根据域名实例化一个InetAddress对象的流程解析。
        比如:InetAddress byName = InetAddress.getByName("wtfu.site");。如果是域名的话,会涉及到系统调用getaddrinfo.里面包括DNS解析。

        1. getAllByName(String host, InetAddress reqAddr)
        2. 不属于ip地址的话,会继续进入到getAllByName0()
        3. addresses = getAddressesFromNameService(host, reqAddr);
        4. addresses = nameService.lookupAllHostAddr(host);
        5. impl.lookupAllHostAddr(host); // impl为Inet6AddressImpl类型,且lookupAllHostAddr为native方法。
        6. 定位到c源码后如下:可以看到关键的getaddrinfo系统调用。

        //src/java.base/unix/native/libnet/Inet6AddressImpl.c
        JNIEXPORT jobjectArray JNICALL
        Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
                                                        jstring host, jint characteristics) {
            error = getaddrinfo(hostname, NULL, &hints, &res);
        }
        
      • 测试系统调用流程 #

        1. 编写样例代码

          #include <sys/types.h>
          #include <sys/socket.h>
          #include <netdb.h>
          #include <stdio.h>
          #include <sys/socket.h>
          #include <netinet/in.h>
          #include <arpa/inet.h>
          #include <string.h>
          
          int main(int argc, char * argv[])
          {
              struct addrinfo hints, *res;
              memset (&hints, 0, sizeof (hints));
              hints.ai_family = AF_INET;
              hints.ai_flags = AI_CANONNAME;
          
              if (0 > getaddrinfo("wtfu.site", NULL, &hints, &res)){
                  perror("get error:");
                  return -1;
              }
              struct sockaddr_in * get_addr = res->ai_addr;
              printf("ip for wtfu: %s\n", inet_ntoa(get_addr->sin_addr));
              return 0;
          }
          
        2. 追踪系统调用

          使用gcc -o test test.c编译源码,然后使用strace ./test 追踪调用。按顺序摘选输出:
          可以看到从/etc/resolv.conf中读取到nameserver,然后使用53端口进行连接查询。

          execve("./test", ["./test"], 0x7ffe34b4bc10 /* 23 vars */) = 0
          open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
          fstat(3, {st_mode=S_IFREG|0644, st_size=1746, ...}) = 0
          mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f553499f000
          read(3, "#\n# /etc/nsswitch.conf\n#\n# An ex"..., 4096) = 1746
          read(3, "", 4096)                       = 0
          close(3)                                = 0
          munmap(0x7f553499f000, 4096)            = 0
          stat("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=89, ...}) = 0
          open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 3
          fstat(3, {st_mode=S_IFREG|0644, st_size=9, ...}) = 0
          mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f553499f000
          read(3, "multi on\n", 4096)             = 9
          read(3, "", 4096)                       = 0
          close(3)                                = 0
          munmap(0x7f553499f000, 4096)            = 0
          open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3
          fstat(3, {st_mode=S_IFREG|0644, st_size=89, ...}) = 0
          mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f553499f000
          read(3, "; generated by /usr/sbin/dhclien"..., 4096) = 89
          read(3, "", 4096)                       = 0
          close(3)                                = 0
          mprotect(0x7f55343aa000, 4096, PROT_READ) = 0
          munmap(0x7f5534998000, 30945)           = 0
          open("/etc/hosts", O_RDONLY|O_CLOEXEC)  = 3
          fstat(3, {st_mode=S_IFREG|0644, st_size=201, ...}) = 0
          mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f553499f000
          read(3, "127.0.0.1 12302 12302\n127.0.0.1 "..., 4096) = 201
          read(3, "", 4096)                       = 0
          close(3)                                = 0
          ...
          socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3
          setsockopt(3, SOL_IP, IP_RECVERR, [1], 4) = 0
          connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("183.60.82.98")}, 16) = 0
          poll([{fd=3, events=POLLOUT}], 1, 0)    = 1 ([{fd=3, revents=POLLOUT}])
          sendto(3, "\342\377\1\0\0\1\0\0\0\0\0\0\4wtfu\4site\0\0\1\0\1", 27, MSG_NOSIGNAL, NULL, 0) = 27
          poll([{fd=3, events=POLLIN}], 1, 5000)  = 1 ([{fd=3, revents=POLLIN}])
          ioctl(3, FIONREAD, [43])                = 0
          recvfrom(3, "\342\377\201\200\0\1\0\1\0\0\0\0\4wtfu\4site\0\0\1\0\1\300\f\0\1\0"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("183.60.82.98")}, [28->16]) = 43
          close(3)                                = 0
          fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
          mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f553499f000
          write(1, "ip for wtfu: 139.155.77.112\n", 28ip for wtfu: 139.155.77.112
          ) = 28
          exit_group(0)                           = ?
          +++ exited with 0 +++
          
      • Reference

    socket #

    • http impl #

      public class Server {
          public static void main(String[] args) {
              // You can use print statements as follows for debugging, they'll be visible when running tests.
              System.out.println("Logs from your program will appear here!");
              ServerSocket serverSocket = null;
              Socket clientSocket = null;
      
              try {
                  serverSocket = new ServerSocket(4221);
                  serverSocket.setReuseAddress(true);
      
                  while(true) {
                      clientSocket = serverSocket.accept(); // Wait for connection from client.
                      System.out.println("accepted new connection");
      
                      Socket finalClientSocket = clientSocket;
                      new Thread(() -> {
                          // 必须关闭,不关闭的话,容易资源泄露,并且curl 会等待。
                          try(finalClientSocket;
                              InputStream input = finalClientSocket.getInputStream();
                              OutputStream output = finalClientSocket.getOutputStream()){
      
                              BufferedReader in = new BufferedReader(new InputStreamReader(input));
                              String startLine = in.lines().findFirst().get();
                              String path = startLine.split(" ")[1];
      
                              StringBuilder response = new StringBuilder();
                              if ("/".equals(path)) {
                                  response.append("HTTP/1.1 200 OK\n");
                              } else {
                                  response.append("HTTP/1.1 404 Not Found\n");
                              }
      
                              response.append("Content-Type: text/plain\n");
                              //response.append("Connection: keep-alive\n");
                              response.append("Connection: close\n");
      
                              response.append("\n");
                              response.append("hello world");
      
                              String responseStr = response.toString();
                              output.write(responseStr.getBytes(StandardCharsets.UTF_8));
                              output.flush();
      
                              System.out.println("This message is sent to the client: \n" + responseStr);
                          }catch (Exception e){e.printStackTrace();}
                      }).start();
                  }
              } catch (Exception e) {
                  System.out.println("IOException: " + e.getMessage());
              } finally {
                  if (serverSocket != null) {
                      try {
                          serverSocket.close();
                      } catch (IOException e) {
                          throw new RuntimeException(e);
                      }
                  }
              }
          }
      }
      
      public class Client {
          public static void main(String[] args) throws Exception {
      
              //Instantiate a new socket
              try (Socket s = new Socket("localhost", 4221);
                  OutputStream outputStream = s.getOutputStream();
                  InputStream inputStream = s.getInputStream()) {
                  //Prints the request string to the output stream
                  StringBuilder req = new StringBuilder();
                  req.append("GET / HTTP/1.1\r\n");
                  req.append("Host: localhost\r\n");
                  req.append("\r\n");
      
                  outputStream.write(req.toString().getBytes(StandardCharsets.UTF_8));
      
                  //Creates a BufferedReader that contains the server response
                  BufferedReader bufRead = new BufferedReader(new InputStreamReader(inputStream));
                  String outStr;
      
                  //Prints each line of the response
                  while((outStr = bufRead.readLine()) != null){
                      System.out.println(outStr);
                  }
              }catch (Exception e){e.printStackTrace();}
          }
      }
      

    channelsocket #

    Reference #


    comments powered by Disqus