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客户端也是类似的情况,就不做重复分析。