数据包Socket API支持离散数据单元(即数据包)交换,流式Socket API则提供了基于UNIX操作系统的流式IO的数据传输模式。
根据定义,流式Socket API仅支持面向连接通信。
流式Socket为两个特定进程提供稳定的数据交换模型。数据流从一方连续写入,从另一方读出。流的特性允许以不同速度向流中写入或读取数据,但是一个流式Socket 不能用于同时与两个及其以上的进程通信。
在Java中,有两个类提供了流式Socket API:ServerSocket和Socket。
1)ServerSocket用于接受连接,称之为连接Socket。
2)Socket用于数据交换,称之为数据Socket。
采用该API,服务器进程建立一个连接Socket,随后侦听来自其他进程的连接请求。每次只接受一个连接请求。当连接被接受后,将为该连接创建一个数据Socket。服务器进程可通过数据Socket从数据流读取数据或向其中写入数据。一旦两进程之间的通信会话结束,数据Socket被关闭,服务器可通过连接Socket自由接收下一个连接请求。
客户进程创建一个Socket,随后通过服务器的连接Socket向服务器发送连接请求。一旦请求被接受,客户Socket与服务器数据Socket连接,以便客户可继续从数据流读取数据或向数据流写入数据。当两进程之间的通信会话结束后,数据Socket关闭。
ServerSocket 的 accept()方法是阻塞操作,如果没有正在等待的请求,服务器进程被挂起,直到连接请求到达。
Socket 的输入流中读取数据时,即InputStream的read()方法是阻塞操作,如果请求的所有数据没有全部到达该输入流中,客户进程将被阻塞,直到有足够数量的数据被写入数据流。
public class SocketServerConnectionAcceptor {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
//服务器监听端口
int portNo=9527;
ServerSocket connectionSocket = new ServerSocket(portNo);
System.out.println("now ready accept a connection~");
//阻塞监听连接
Socket dataSocket=connectionSocket.accept();
//可以从socket里获得发送方的地址和端口,以及接收方的地址和端口
System.out.println(dataSocket.getInetAddress()+" "+dataSocket.getPort()+" "
+dataSocket.getRemoteSocketAddress()+" "+dataSocket.getLocalPort()+" "
+dataSocket.getLocalAddress());
System.out.println("connection accepted:");
OutputStream outputStream=dataSocket.getOutputStream();
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream));
for(int i=0;i<5;i++) {
String msg=scanner.nextLine();
printWriter.println(msg);
printWriter.flush();
}
System.out.println("message sent!!");
//先关掉数据Socket
dataSocket.close();
System.out.println("data socket closed!");
//再关掉连接Socket
connectionSocket.close();
System.out.println("connection socket closed!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class SocketConnectionRequestor {
public static void main(String []args) {
try {
//服务器地址和端口号
InetAddress acceptorHost = InetAddress.getByName("127.0.0.1");
int acceptorPort=9527;
SocketAddress socketAddress = new InetSocketAddress(acceptorHost, acceptorPort);
//数据Socket
Socket mySocket = new Socket();
//最大等待连接时长
int timeoutPeriod=5000;
mySocket.connect(socketAddress,timeoutPeriod);
System.out.println("Connection request granted!!");
//可以从socket里获得发送方的地址和端口,以及接收方的地址和端口
System.out.println(mySocket.getInetAddress()+" "+mySocket.getPort()+" "
+mySocket.getRemoteSocketAddress()+" "+mySocket.getLocalPort()+" "
+mySocket.getLocalAddress());
InputStream inputStream=mySocket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
System.out.println("waiting to read!!");
for(int i=0;i<5;i++) {
String msg=bufferedReader.readLine();
System.out.println("Message received:");
System.out.println("\t"+msg);
}
mySocket.close();
System.out.println("data socket closed!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上是服务器写数据,客户端读数据;其实完全可以双方都写、读。
当使用PrintWriter向 Socket流写入数据时,必须使用flush()方法调用来真正地填充与刷新该流,从而确保所有数据都可以在像Socket突然关闭等意外情形发生之前,尽可能快地从数据缓冲区中真正地写入数据流。
为允许将程序中的应用逻辑和服务逻辑分离,采用了隐藏数据Socket细节的子类。
public class MyStreamSocket{
private Socket socket;
private BufferedReader input;
private PrintWriter output;
MyStreamSocket(String acceptorHost, int acceptorPort ) throws SocketException, IOException{
socket = new Socket(acceptorHost, acceptorPort );
setStreams();
}
MyStreamSocket(Socket socket) throws IOException {
this.socket = socket;
setStreams();
}
private void setStreams( ) throws IOException{
// 从输入流中获取inputStream
InputStream inStream = socket.getInputStream();
input = new BufferedReader(new InputStreamReader(inStream));
OutputStream outStream = socket.getOutputStream();
// 创建一个PrintWriter来操作字符流
output = new PrintWriter(new OutputStreamWriter(outStream));
}
public void sendMessage(String message) throws IOException {
output.println(message);
//flush操作为了避免意外关闭socket而导致数据丢失
output.flush();
} // end sendMessage
public String receiveMessage( ) throws IOException {
// 从输入流读取一行
return input.readLine( );
} //end receiveMessage
public void SocketClose() throws IOException{
socket.close();
}
} //end class
public class ServerSocketConnectionAcceptor {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int portNo=9527;
ServerSocket serverSocket = new ServerSocket(portNo);
System.out.println("now ready accept a connection~");
MyStreamSocket dataSocket = new MyStreamSocket(serverSocket.accept());
System.out.println("connection accepted:");
for(int i=0;i<5;i++) {
dataSocket.sendMessage(scanner.nextLine());
}
dataSocket.SocketClose();
System.out.println("data socket closed!");
serverSocket.close();
System.out.println("connection socket closed!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class SocketConnectionRequestor {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int serverPortNo=9527;
String serverHost = "127.0.0.1";
MyStreamSocket dataSocket = new MyStreamSocket(serverHost,serverPortNo);
System.out.println("connection request granted:");
for(int i=0;i<5;i++) {
System.out.println(dataSocket.receiveMessage());
}
dataSocket.SocketClose();
System.out.println("data socket closed!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上是服务器为单线程,若加入多线程:
public class MyStreamSocket implements Runnable{
private Socket socket;
private BufferedReader input;
private PrintWriter output;
MyStreamSocket(String acceptorHost, int acceptorPort ) throws SocketException, IOException{
socket = new Socket(acceptorHost, acceptorPort );
setStreams();
}
MyStreamSocket(Socket socket) throws IOException {
this.socket = socket;
setStreams();
}
......
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
try {
System.out.println("connection accepted:");
for(int i=0;i<5;i++) {
sendMessage(scanner.nextLine());
}
SocketClose();
System.out.println("data socket closed!");
} catch (Exception e) {
e.printStackTrace();
}
}
} //end class
public class ServerSocketThreadConnectionAcceptor {
public static void main(String[] args) {
try {
int portNo=9527;
ServerSocket serverSocket = new ServerSocket(portNo);
System.out.println("now ready accept a connection~");
while (true) {
Socket socket=serverSocket.accept();
new Thread(new MyStreamSocket(socket)).start();
}
// serverSocket.close();
// System.out.println("connection socket closed!");
} catch (Exception e) {
e.printStackTrace();
}
}
}