Java IO 分为文件IO和网络IO,文件IO主要是操作文件的读写,网络IO则是面向socket套接字用于处理来自网络上请求..
网络套接字是计算机网络的网络节点内的一种软件结构,它用于通过网络发送和接收数据的端点(IP地址+端口)。
通用的处理逻辑如下所示:
- 使用 socket 创建socket
- 使用 bind 绑定到一个本地地址支持其他socket使用 connect 来链接
- 使用 listen 接受即将到来的链接和一个限制链接数的队列.
- 使用 accept 来接受链接
简单实例
public class Test {final Logger log = LoggerFactory.getLogger(this.getClass());public static void main(String[] args) throws Exception {ServerSocket socket = new ServerSocket();socket.bind(new InetSocketAddress("0.0.0.0", 9999));while (true) {handle(socket.accept());}}static void handle(Socket socket) throws IOException {final PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));String inputLine;while ((inputLine = bufferedReader.readLine()) != null) {if (".".equals(inputLine)) {printWriter.println("bye");break;}printWriter.println(inputLine);printWriter.flush();}printWriter.close();bufferedReader.close();socket.close();}}
ServerSocket,代表服务端套接字,等待请求到来,基于请求处理并响应InetSocketAddress代表了IP套接字地址(IP地址+端口)create创建一个socketbind一个IP和端口并且监听accept该方法将阻塞,直到建立连接为止- 从socket中读取数据或者写入数据到socket中. 接受到
.后关闭链接.
source code ./src/solaris/native/java/net/PlainSocketImpl.c
源代码
public void bind(SocketAddress endpoint, int backlog) throws IOException {try {//getImpl()//--- 内部执行socketCreate,系统调用 https://linux.die.net/man/2/socket//check ...//JNI调用后执行系统调用(system call) bind//java.net.PlainSocketImpl.socketBindgetImpl().bind(epoint.getAddress(), epoint.getPort());//JNI调用执行系统调用(listen)//java.net.PlainSocketImpl.socketListengetImpl().listen(backlog);bound = true;} catch(SecurityException e) {bound = false;throw e;} catch(IOException e) {bound = false;throw e;}}
java.net.PlainSocketImpl.socketBind
+552 PlainSocketImpl.c
/** Class: java_net_PlainSocketImpl* Method: socketBind* Signature: (Ljava/net/InetAddress;I)V*/JNIEXPORT void JNICALLJava_java_net_PlainSocketImpl_socketBind(JNIEnv *env, jobject this,jobject iaObj, jint localport) {/* fdObj is the FileDescriptor field on this */jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);/* fd is an int field on fdObj */int fd;int len;SOCKADDR him;if (IS_NULL(fdObj)) {JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException","Socket closed");return;} else {fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);}if (IS_NULL(iaObj)) {JNU_ThrowNullPointerException(env, "iaObj is null.");return;}/* bind */if (NET_InetAddressToSockaddr(env, iaObj, localport, (struct sockaddr *)&him, &len, JNI_TRUE) != 0) {return;}setDefaultScopeID(env, (struct sockaddr *)&him);if (NET_Bind(fd, (struct sockaddr *)&him, len) < 0) {if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||errno == EPERM || errno == EACCES) {NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "BindException","Bind failed");} else {NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException","Bind failed");}return;}/* set the address */(*env)->SetObjectField(env, this, psi_addressID, iaObj);/* initialize the local port */if (localport == 0) {/* Now that we're a connected socket, let's extract the port number* that the system chose for us and store it in the Socket object.*/if (JVM_GetSockName(fd, (struct sockaddr *)&him, &len) == -1) {NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException","Error getting socket name");return;}localport = NET_GetPortFromSockaddr((struct sockaddr *)&him);(*env)->SetIntField(env, this, psi_localportID, localport);} else {(*env)->SetIntField(env, this, psi_localportID, localport);}}
/** Wrapper for bind system call - performs any necessary pre/post* processing to deal with OS specific issues :-** Linux allows a socket to bind to 127.0.0.255 which must be* caught.** On Solaris with IPv6 enabled we must use an exclusive* bind to guarantee a unique port number across the IPv4 and* IPv6 port spaces.**/intNET_Bind(int fd, struct sockaddr *him, int len){//... linuxrv = bind(fd, him, len);return rv;}
java.net.PlainSocketImpl.socketAccept
//-- fcntl - manipulate file descriptor#define SET_BLOCKING(fd) { \int flags = fcntl(fd, F_GETFL); \flags &= ~O_NONBLOCK; \fcntl(fd, F_SETFL, flags); \}JNIEXPORT void JNICALLJava_java_net_PlainSocketImpl_socketAccept(JNIEnv *env, jobject this,jobject socket){//填充参数for (;;) {int ret;//check and accept 系统调用newfd = NET_Accept(fd, (struct sockaddr *)&him, (jint*)&len);/* connection accepted */if (newfd >= 0) {//设置阻塞 内部是SET_BLOCKING(newfd);break;}/* non (ECONNABORTED or EWOULDBLOCK) error */if (!(errno == ECONNABORTED || errno == EWOULDBLOCK)) {break;}//check}//check/** 在新socket结构中填充remote 端口和地址*/socketAddressObj = NET_SockaddrToInetAddress(env, (struct sockaddr *)&him, &port);if (socketAddressObj == NULL) {/* should be pending exception */close(newfd);return;}/** Populate SocketImpl.fd.fd* 填充文件描述符*/socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID);(*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd);(*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj);(*env)->SetIntField(env, socket, psi_portID, port);/* also fill up the local port information */port = (*env)->GetIntField(env, this, psi_localportID);(*env)->SetIntField(env, socket, psi_localportID, port);}
在linux中,一切都是文件,操作socket也是读写文件,所以在经历bind/accept等经历后,可以在serverSocket和socket上的文件描述符进行读写.
