开篇简单说下 nio 和 老io 除了reactor的区别
同步的 socket tcp三次握手比( 1.socketclient 连接 2.socket服务端接受连接和(请求内容,也可以ignore)发送response 3.socketclient read服务端发送的response 这三个步骤是阻塞的 阻塞在read上。)
而nio 中, channel client不用阻塞的去read, 可以采用 selecter去 select到READ请求, 再从selectedKeys中拿到 对应的channel 去读数据。
其实这边channel 和 hadoop rpc 异步 的call 设计的比较像
——————
服务端:
<pre name="code" class="html">import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer2 {
public TCPServer2() {
}
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket ss = new ServerSocket(6666);
while(true){
Socket s = ss.accept();
DataInputStream dis = new DataInputStream(s.getInputStream());
System.out.println("server1 debug");
System.out.println("server:"+ dis.readUTF());
System.out.println("server2 debug");
DataOutputStream dos =new DataOutputStream(s.getOutputStream());
Thread.sleep(2000);
dos.writeUTF("client's ip & port:"+ s.getInetAddress() +"__" + s.getPort());//这边是阻塞的,client 没有写 没有flush,这边会一直会block。 这个时候 其他线程进不来
System.out.println("write done");
dos.flush();
dos.close();// 注意 无论是dos 还是 dis invoke close方法,都会将当前方法栈 socket 关闭 ,不过while里又new 出来个新的
}
}
}
客户端
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient2 {
public TCPClient2() {
}
public static void main(String[] args) throws UnknownHostException,
IOException, InterruptedException {
Socket s = new Socket("127.0.0.1", 6666);// sever connect
System.out.println("a client connect from client!");
//debug
DataOutputStream dos =new DataOutputStream(s.getOutputStream());
dos.writeUTF("i am client");
dos.flush();
//dos.close();// 注意 无论是dos 还是 dis invoke close方法,都会将当前方法栈 socket 关闭
//
DataInputStream dis = new DataInputStream(s.getInputStream());
System.out.println(dis.readUTF());// 这边是阻塞的,client 没有写
// 没有flush,这边会一直会block。 这个时候 其他线程进不来
s.close();
}
}
在 双方连接 某一方写 某一方读到 这个过程都是block的。
readUTF() 操作是阻塞的,一定要对边写完,这边才能读到,否则block在那。
上边实例的简单流程是是:
1.服务端启动后: while socket 去accept() 阻塞操作
2.一旦有client有连接后 即serversocket.accept()后,读取 客户端的请求内容(凡是read操作都是阻塞的,一端写另一端才能读,否则就阻塞了)(当然服务端可以不读客户端的请求内容直接返回,客户端也可以连接后不写请求内容,写操作是非阻塞的,写后立刻返回),再通过socket输出流 返回数据
3.client 可以读取内容后,关闭socket 结束。
另外如果客户端写了两次, 无所谓 1次flush 还是2次
dos.writeUTF("i am client1");
dos.flush();
dos.writeUTF("i am client2");
dos.flush();
客户端要读两次 (不懂 现象是这样)
System.out.println("server:"+ dis.readUTF());
System.out.println("server:"+ dis.readUTF());
另外客户端写的时候,必须在接受服务端之前一次写完,如果之后再写 就要new 一个新的socket。 因为服务端再 从accpet 到 write的过程中,完成了对那个client socket的整个处理。
——————
在socket 连接之后 :
如果socket的两端都是 读input流,卡死
如果socket的两端都是 写output流,卡死
需要 一边是写 一边是读 才会返回. 这也是要满足 tcp 三次握手的原则, 才算完成。
——————————
另外注: 客户端 向固定服务端 发消息时,会随机取一个端口 和 服务端固定端口 通讯,
可以通过在服务端 Socket s = ss.accept(); s 去get对应客户端的port
————————————
TCP/IP网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路层(Link)四层。如下图所示
两台计算机通过TCP/IP协议通讯的过程如下所示