java实现hello/hi的简单的网络聊天程序与ServerSocket调用栈跟踪

java实现hello/hi的简单的网络聊天程序

  • 网络聊天采用TCP协议通过java实现

    import java.io.*;
    import java.net.Socket;
    
    public class Client {
        public static void main(String[] args) throws Exception{
          
            Socket socket = new Socket("192.168.31.68", 6666);
    
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            String s = reader.readLine();
    
            OutputStream outputStream = socket.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
            writer.write(s);
            writer.newLine();
            writer.flush();
            socket.close();
        }
    }
    
    
    import java.io.InputStream;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    
    public class SeverDemo {
        public static void main(String[] args) throws Exception{
         
            ServerSocket serverSocket = new ServerSocket(6666);
            Object i = null;
           
            while(true) {
                Socket socket = serverSocket.accept(); 
               
                InputStream inputStream = socket.getInputStream();
                
                byte[] bytes = new byte[1024];
                int len = inputStream.read(bytes);
                String s = new String(bytes, 0, len);
                InetAddress address = socket.getInetAddress();
                System.out.println("from " + address.getHostAddress() + ": " + s);
             
                socket.close();
            }
        }
    
    
    
  • Server

  • Client

ServerSocket调用栈跟踪

  • 该图片描述的是socket0函数调用栈的关系
  • 从图中可以看出java从 serversocket -> 调用socketImpt -> abstractPlainSocketImpl -> DualStackSocketPlainImpl-> DualStackPlainImplt_socket0
  • 最后调用的是java中的native函数,可以在openjdk中看到(我使用的版本是openjdk 8.0)
// windows/native/java/net/DualStackPlainSocketImpl.c
/*
 * Class:     java_net_DualStackPlainSocketImpl
 * Method:    socket0
 * Signature: (ZZ)I
 */
JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_socket0
  (JNIEnv *env, jclass clazz, jboolean stream, jboolean v6Only /*unused*/) {
    int fd, rv, opt=0;
  
//细看可以发现,这就是我们熟悉的linux下网络编程socket的api
    fd = NET_Socket(AF_INET6, (stream ? SOCK_STREAM : SOCK_DGRAM), 0);
  
  	if (fd == INVALID_SOCKET) {
        NET_ThrowNew(env, WSAGetLastError(), "create");
        return -1;
    }

    rv = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt));
    if (rv == SOCKET_ERROR) {
        NET_ThrowNew(env, WSAGetLastError(), "create");
    }

    SetHandleInformation((HANDLE)(UINT_PTR)fd, HANDLE_FLAG_INHERIT, FALSE);

    return fd;
}

  • 该图片是listen函数的调用栈关系

  • ServerSocket -> abstractPlaninSocketImpl -> DualStackSocketPlainImpl-> DualStackPlainImplt_listen0

  • native关键字声明的c源码如下

    /*
     * Class:     java_net_DualStackPlainSocketImpl
     * Method:    listen0
     * Signature: (II)V
     */
    JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_listen0
      (JNIEnv *env, jclass clazz, jint fd, jint backlog) {
        if (listen(fd, backlog) == SOCKET_ERROR) {
            NET_ThrowNew(env, WSAGetLastError(), "listen failed");
        }
    }
    
    

  • 该图片为bind函数的调用栈关系
  • ServerSocket -> abstractPlaninSocketImpl -> DualStackSocketPlainImpl-> DualStackPlainImplt_bind0
  • native关键字声明的c源码如下
/*
 * Class:     java_net_DualStackPlainSocketImpl
 * Method:    bind0
 * Signature: (ILjava/net/InetAddress;I)V
 */
JNIEXPORT void JNICALL Java_java_net_DualStackPlainSocketImpl_bind0
  (JNIEnv *env, jclass clazz, jint fd, jobject iaObj, jint port,
   jboolean exclBind)
{
    SOCKETADDRESS sa;
    int rv;
    int sa_len = sizeof(sa);

    if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&sa,
                                 &sa_len, JNI_TRUE) != 0) {
      return;
    }

    rv = NET_WinBind(fd, (struct sockaddr *)&sa, sa_len, exclBind);

    if (rv == SOCKET_ERROR)
        NET_ThrowNew(env, WSAGetLastError(), "JVM_Bind");
}

  • 该图片为accept函数的调用栈关系
  • ServerSocket -> abstractPlaninSocketImpl -> DualStackSocketPlainImpl-> DualStackPlainImplt_accept0
JNIEXPORT jint JNICALL Java_java_net_DualStackPlainSocketImpl_accept0
  (JNIEnv *env, jclass clazz, jint fd, jobjectArray isaa) {
    int newfd, port=0;
    jobject isa;
    jobject ia;
    SOCKETADDRESS sa;
    int len = sizeof(sa);

    memset((char *)&sa, 0, len);
    newfd = accept(fd, (struct sockaddr *)&sa, &len);

    if (newfd == INVALID_SOCKET) {
        if (WSAGetLastError() == -2) {
            JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
                            "operation interrupted");
        } else {
            JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
                            "socket closed");
        }
        return -1;
    }

    ia = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa, &port);
    isa = (*env)->NewObject(env, isa_class, isa_ctorID, ia, port);
    (*env)->SetObjectArrayElement(env, isaa, 0, isa);

    return newfd;
}

Serversocket总结

  • java底层也是调用linux 网络api实现网络通信的
  • java中Serversocket创建时就对函数进行了socket和bind和listen操作,一个函数封装了linux下3个api
  • java中的accept对应与linux下accpet函数
  • Java中Socket客户端也是类似的情况,就不做重复分析。

posted on 2019-12-07 15:47  humanyang  阅读(286)  评论(2编辑  收藏  举报

导航