Java Socket编程
对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了。首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信。这样就有两个Socket了,客户端和服务端各一个。
对于Socket之间的通信其实很简单,服务端往Socket的输出流里面写东西,客户端就可以通过Socket的输入流读取对应的内容。Socket与Socket之间是双向连通的,所以客户端也可以往对应的Socket输出流里面写东西,然后服务端对应的Socket的输入流就可以读出对应的内容。下面来看一些服务端与客户端通信的例子:
1.客户端写服务端读。
服务端代码
import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.ServerSocket; import java.net.Socket; public class Server { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { int port = 8899; // 定义一个ServerSocket监听在端口8899上 ServerSocket server = new ServerSocket(port); // server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的 Socket socket = server.accept(); // 跟客户端建立好连接之后,我们就可以获取socket的InputStream,并从中读取客户端发过来的信息了。 Reader reader = new InputStreamReader(socket.getInputStream()); char chars[] = new char[64]; int len; StringBuilder sb = new StringBuilder(); while ((len = reader.read(chars)) != -1) { sb.append(new String(chars, 0, len)); } System.out.println("from client: " + sb); reader.close(); socket.close(); server.close(); } }
服务端从Socket的InputStream中读取数据的操作也是阻塞式的,如果从输入流中没有读取到数据程序会一直在那里不动,直到客户端往Socket的输出流中写入了数据,或关闭了Socket的输出流。当然,对于客户端的Socket也是同样如此。在操作完以后,整个程序结束前记得关闭对应的资源,即关闭对应的IO流和Socket。
客户端代码
import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.Socket; import java.net.UnknownHostException; public class Client { /** * @param args * @throws IOException * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException, IOException { 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.flush();// 写完后要记得flush writer.close(); client.close(); } }
对于客户端往Socket的输出流里面写数据传递给服务端要注意一点,如果写操作之后程序不是对应着输出流的关闭,而是进行其他阻塞式的操作(比如从输入流里面读数据),记住要flush一下,只有这样服务端才能收到客户端发送的数据,否则可能会引起两边无限的互相等待。另外前面已经说了Socket之间是双向通信的,它既可以接收数据,同时也可以发送数据。
2.多客户端连接到一个服务器
服务端接收一个客户端的请求之后就结束了,不能再接收其他客户端的请求了,这往往是不能满足我们的要求的。下面我们结合多线程,让一个服务器同时接收多个客户端信息,同时输出信息到客户端。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.ServerSocket; import java.net.Socket; public class Server { /** * @param args * @throws IOException */ 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(); } } } class Task implements Runnable{ private Socket socket; public Task(Socket socket) { this.socket = socket; } @Override public void run() { handleSocket(); } private void handleSocket(){ try { BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"GBK")); 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); } String str = sb.toString(); String name = str.substring(str.indexOf("[")+1,str.indexOf("]")); String content = str.substring(str.indexOf("]")+1,str.length()); System.out.println("from client["+name+"]:" + content); //读完后写一句 Writer writer = new OutputStreamWriter(socket.getOutputStream(),"UTF-8"); writer.write("你好,"+name); writer.write("eof\n"); writer.flush(); writer.close(); br.close(); socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; public class Client { /** * @param args * @throws IOException * @throws UnknownHostException */ public static void main(String[] args){ String[] names = {"小红","小明","小黄","小兰","jeck","mako"}; for(int i=0;i<names.length;i++){ createClient(names[i]); } } public static void createClient(String name){ String host = "127.0.0.1"; //要连接的服务端IP地址 int port = 8899; //要连接的服务端对应的监听端口 //与服务端建立连接 Socket client; try { client = new Socket(host, port); //建立连接后就可以往服务端写数据了,这里用GBK写入,服务器也要用GBK接收,否则会中文乱码 Writer writer = new OutputStreamWriter(client.getOutputStream(), "GBK"); writer.write("["+name+"]"); writer.write("你好,服务端"); writer.write("eof\n"); writer.flush(); //写完以后进行读操作,这里用UTF-8读取,服务器也要用UTF-8写,否则会中文乱码 BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); //设置超时间为10秒,设置接收数据的超时时间,单位是毫秒。当设置的超时时间大于0,并且超过了这一时间Socket还没有接收到返回的数据的话,Socket就会抛出一个SocketTimeoutException。 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("read the date time out。"); } System.out.println("from server: " + sb); writer.close(); br.close(); client.close(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }