Java Socket编程
对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了。首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信。这样就有两个Socket了,客户端和服务端各一个。
对于Socket之间的通信其实很简单,服务端往Socket的输出流里面写东西,客户端就可以通过Socket的输入流读取对应的内容。Socket与Socket之间是双向连通的,所以客户端也可以往对应的Socket输出流里面写东西,然后服务端对应的Socket的输入流就可以读出对应的内容。下面来看一个服务端与客户端通信的例子:
多个客户端连接同一个服务端
每次 ServerSocket 接收到一个新的 Socket 连接请求后都会新起一个线程来跟当前 Socket 进行通信,这样就达到了异步处理与客户端 Socket 进行通信的情况。我们使用BufferedReader来一次读一行,而不是一次读多少字节
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String args[]) throws IOException {
//为了简单起见,所有的异常信息都往外抛
int port = 8899;
//定义一个ServerSocket监听在端口8899上
ServerSocket server = new ServerSocket(port);
while (true) {
//server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的
Socket socket = server.accept();
//每接收到一个Socket就建立一个新的线程来处理它
new Thread(new Task(socket)).start();
}
}
/**
* 用来处理Socket请求的
*/
static class Task implements Runnable {
private Socket socket;
public Task(Socket socket) {
this.socket = socket;
}
public void run() {
try {
handleSocket();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 跟客户端Socket进行通信
* @throws Exception
*/
private void handleSocket() throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder sb = new StringBuilder();
String temp;
int index;
while ((temp=br.readLine()) != null) {
System.out.println(temp);
if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收
sb.append(temp.substring(0, index));
break;
}
sb.append(temp);
}
System.out.println("from client: " + sb);
//读完后写一句
Writer writer = new OutputStreamWriter(socket.getOutputStream());
writer.write("Hello Client.");
writer.write("eof\n");
writer.flush();
writer.close();
br.close();
socket.close();
}
}
}
这个时候需要注意的是, BufferedReader 的 readLine 方法是一次读一行的 ,这个方法是阻塞的,直到它读到了一行数据为止程序才会继续往下执行,那么 readLine 什么时候才会读到一行呢?直到程序遇到了换行符或者是对应流的结束符 readLine 方法才会认为读到了一行,才会结束其阻塞 ,让程序继续往下执行。所以我们在使用 BufferedReader 的 readLine 读取数据的时候一定要记得在对应的输出流里面一定要写入换行符(流结束之后会自动标记为结束, readLine 可以识别),写入换行符之后一定记得如果输出流不是马上关闭的情况下记得 flush 一下,这样数据才会真正的从缓冲区里面写入。对应上面的代码我们的客户端程序应该这样写:
客户端:
假设有这样一种需求,我们的客户端需要通过Socket从服务端获取到XX信息,然后给用户展示在页面上。我们知道Socket在读数据的时候是阻塞式的,如果没有读到数据程序会一直阻塞在那里。在同步请求的时候我们肯定是不能允许这样的情况发生的,这就需要我们在请求达到一定的时间后控制阻塞的中断,让程序得以继续运行。Socket为我们提供了一个setSoTimeout()方法来设置接收数据的超时时间,单位是毫秒。当设置的超时时间大于0,并且超过了这一时间Socket还没有接收到返回的数据的话,Socket就会抛出一个SocketTimeoutException。
假设我们需要控制我们的客户端在开始读取数据10秒后还没有读到数据就中断阻塞的话我们可以这样做:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;
import java.net.SocketTimeoutException;
public class Client {
public static void main(String args[]) throws Exception {
//为了简单起见,所有的异常都直接往外抛
String host = "127.0.0.1"; //要连接的服务端IP地址
int port = 8899; //要连接的服务端对应的监听端口
//与服务端建立连接
Socket client = new Socket(host, port);
//建立连接后就可以往服务端写数据了
Writer writer = new OutputStreamWriter(client.getOutputStream());
writer.write("Hello Server.");
writer.write("eof\n");
writer.flush();
//写完以后进行读操作
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
//设置超时间为10秒
client.setSoTimeout(10*1000);
StringBuffer sb = new StringBuffer();
String temp;
int index;
try {
while ((temp=br.readLine()) != null) {
if ((index = temp.indexOf("eof")) != -1) {
sb.append(temp.substring(0, index));
break;
}
sb.append(temp);
}
} catch (SocketTimeoutException e) {
System.out.println("数据读取超时。");
}
System.out.println("from server: " + sb);
writer.close();
br.close();
client.close();
}
}
真的很感谢写这篇文字的博主,转载自http://haohaoxuexi.iteye.com/blog/1979837 可以查看原文更详细的了解一下