程序分成2个部分:
1,服务端,用来接受客户端发来的信息
2,客户端,用来向服务端发信息。
一、服务端如下:
SocketServerThread.java用来处理客户端发送的信息
package com.thread.socket.service; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; public class SocketServerThread extends Thread { // 和本线程相关的Socket Socket socket = null; public SocketServerThread(Socket socket) { this.socket = socket; } @Override public void run() { InputStream is = null; InputStreamReader isr = null; BufferedReader br = null; OutputStream os = null; PrintWriter pw = null; //获取输入流,并读取客户端信息 try { is = socket.getInputStream(); isr = new InputStreamReader(is); br = new BufferedReader(isr); String info = null; while((info=br.readLine()) != null) { //循环读取客户端的信息 System.out.println(info); } socket.shutdownInput(); //关闭输入流 // 获取输出流,响应客户端的请求 os = socket.getOutputStream(); pw = new PrintWriter(os); } catch (IOException e) { e.printStackTrace(); } finally { //关闭资源 try { if(pw!=null) pw.close(); if(os!=null) os.close(); if(br!=null) br.close(); if(isr!=null) isr.close(); if(is!=null) is.close(); if(socket!=null) socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
ServerThread.java用来监听客户端请求,接受请求后开启线程调度SocketServerThread.java
package com.thread.socket.service; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * 用来接受客户端发送的信息 * */ public class ServerThread extends Thread { @SuppressWarnings("resource") @Override public void run() { // 1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口 try { ServerSocket serverSocket = new ServerSocket(8888); Socket socket = null; System.out.println("***服务器即将启动,等待客户端的连接***"); // 循环监听等待客户端的连接 while(true) { // 调用accept()方法开始监听,等待客户端的连接 socket = serverSocket.accept(); // 创建一个新的线程,用来处理请求 SocketServerThread serverThread = new SocketServerThread(socket); // 启动线程 serverThread.start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
二、客户端如下:
Client.java
package com.thread.socket.client; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class Client { /** * 向服务端发送信息 * * @param str */ public static void output(String str) { try { // 1.创建客户端Socket,指定服务器地址和端口 Socket socket = new Socket("localhost", 8008); // TODO 这里用的是本地的IP // 2.获取输出流,向服务器端发送信息 OutputStream os = socket.getOutputStream(); PrintWriter pw = new PrintWriter(os); pw.write(str); pw.flush(); socket.shutdownOutput();//关闭输出流 //4.关闭资源 pw.close(); os.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
三、启动服务端
package com.thread.socket.main; import java.util.Scanner; import com.thread.socket.client.Client; import com.thread.socket.service.ServerThread; public class Chat { @SuppressWarnings("resource") public static void main(String[] args) { // 启动服务器 ServerThread s = new ServerThread(); s.start(); // 向服务器发送信息 while (true) { Scanner sb = new Scanner(System.in); System.out.print("输入语句:"); String str = sb.nextLine(); Client.output(str); } } }
注意:上面几个文件放在一起算一个程序。
将上述文件复制一份,做成另一个程序,开启这两个程序,就可以相互聊天了。
如果想在本地让这两程序相互发送消息的话,其中的一个程序的服务端要需要修改监听端口号,
另外一个程序的客户端要更改发送的端口号,这样两个程序才能在本地相互发送消息;
否则启动好一个程序后,再启动另一个程序,就会报该端口号被占用的错误。
---------------------------下面是本人搞事时,发现的bug-----------------------------
另外本人尝试连续不断的向服务端发送10000的请求,发现报错了
注意:设置成10000时,有时候会报错,不一定是每次都报错,如果想每次报错把这个10000改成100w试试。
报错信息如下:java.net.BindException: Address already in use: connect
这是别人的解释:https://blog.csdn.net/ywb201314/article/details/51258777
大致原因是端口号来不及释放,导致错误。
------------------------------------------------------------------------------------------
我多次测试之后,感觉应该是服务端接受请求上限导致的。
测试结果:服务端同时能接受的请求的上限是50条,
ServerSocket类部分源码定义如下(大概在229行左右):
public ServerSocket(int port) throws IOException { this(port, 50, null); } public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException { setImpl(); if (port < 0 || port > 0xFFFF) throw new IllegalArgumentException( "Port value out of range: " + port); if (backlog < 1) backlog = 50; try { bind(new InetSocketAddress(bindAddr, port), backlog); } catch(SecurityException e) { close(); throw e; } catch(IOException e) { close(); throw e; } }
backlog这个参数解释是:请求链接队列的最大长度。
当你直接 new ServerSocket(port)时,默认是50。
从上面构造可以看出请求链接最大不能超过0xFFFF=2^16-1=65535