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
创建一个socket
bind
一个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.socketBind
getImpl().bind(epoint.getAddress(), epoint.getPort());
//JNI调用执行系统调用(listen)
//java.net.PlainSocketImpl.socketListen
getImpl().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 JNICALL
Java_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.
*
*/
int
NET_Bind(int fd, struct sockaddr *him, int len)
{
//... linux
rv = 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 JNICALL
Java_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上的文件描述符进行读写.