以前在工做中写过一些Socket客户端与服务端的代码,可是当时没有时间仔细研究,只能不报错先过的态度,对其细节了解不深,写的代码有各类问题也浑然不知,只是业务量级以及对接方对接代码没有出现出格的状况因此问题不得暴露。app
首先经过单线程Socket作服务端是一种BIO的作法,这种作法会致使服务端只能同时接收一笔请求,性能很是差dom
下面我把BIO的代码帖一下,有须要的同窗能够参考socket
public class SocketIO { //客户端编码,客户端发送编码与服务端一致,则服务端无需进行解码特殊处理 private static final String CLIENTENCODEING = "UTF-8"; private static final int PORT = 7777; private static AtomicInteger count = new AtomicInteger(); public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(PORT); System.out.println("端口启动:"+PORT); while (true) { Socket socket = null; InputStream inputStream = null; OutputStream outputStream = null; try { //无数据时会阻塞在这里,有数据即accept事件会触发accept方法的调用 //当一个链接进来,第二个链接进不来,由于单线程时,还阻塞在下面的read,线程没法再回到accept上继续等待 socket = serverSocket.accept(); int ccount = count.incrementAndGet(); System.out.println("新的客户端已链接,当前No."+ccount+" "+System.currentTimeMillis()); inputStream = new BufferedInputStream(socket.getInputStream()); outputStream = new BufferedOutputStream(socket.getOutputStream()); //读取正文内容 byte[] flush = new byte[1024]; int length = 0; StringBuffer rec = new StringBuffer(); while ((length = inputStream.read(flush)) != -1) { rec.append(new String(flush, 0, length)); } //写法2 //客户端不通知关闭socket.shutdownOutput();时,用下面这种方法,不通知关闭read方法会死循环 //available()方法能够在读写操做前先得知数据流里有多少个字节能够读取 //但若是客户端分批发送可能有问题,可能没法得到第二批及之后的数据 //因此最好仍是让客户端通知一下 // int count = 0; // while(count == 0){ // count = inputStream.available(); // } // byte[] flush = new byte[count]; // inputStream.read(flush); // String rec = new String(flush, 0, count, CLIENTENCODEING); String back = "["+ccount+"]"+UUID.randomUUID() + ""; System.out.println("收到数据:" + rec.toString() + " 即将返回数据:" + back); //返回数据 outputStream.write(back.getBytes(), 0, back.getBytes().length); outputStream.flush(); } catch (Exception e) { e.printStackTrace(); } finally { close(socket,inputStream,outputStream); } } } private static void close(Socket socket, InputStream inputStream, OutputStream outputStream) throws IOException { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } if (socket != null) { socket.close(); } } }
列几点须要注意的点:性能
一、最好经过read方法来读取客户端发来的内容,代码中available我实测客户端发送屡次的状况下会丢消息,不可靠。编码
二、最好经过缓冲流Buffered*Stream来配合InputStream、OutputStream使用spa
三、write后记得flush线程
四、关闭流code
//服务端编码 private static final String SERVERENCODEING = "UTF-8"; public static void main(String[] args){ for (int i = 0; i < 1; i++) { new Thread(() -> { try { doo(); } catch (IOException e) { e.printStackTrace(); } }).start(); } } public static void doo() throws IOException { Socket socket = null; InputStream in = null; OutputStream out = null; String msg = "你好你好你好好好!"; try { //发送数据 socket = new Socket("127.0.0.1", 7777); out = new BufferedOutputStream(socket.getOutputStream()); in = new BufferedInputStream(socket.getInputStream()); out.write(msg.getBytes()); out.flush(); out.write("还有点".getBytes()); out.flush(); // //任何的输入流或输出流的close()都会形成Socket关闭 可是不关闭又致使服务端没法接收到-1 // out.close(); socket.shutdownOutput(); //读取正文内容 byte[] flush = new byte[1024]; int length = 0; StringBuffer rec = new StringBuffer(); while ((length = in.read(flush)) != -1) { rec.append(new String(flush, 0, length, SERVERENCODEING));//以服务端编码标准发送 } System.out.println("客户端收到回复:" + rec.toString()); in.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } finally { close(socket, in, out); } } private static void close(Socket socket, InputStream inputStream, OutputStream outputStream) throws IOException { if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } if (socket != null) { socket.close(); } }
列几点须要注意的点:server
一、out.close();任何的输入流或输出流的close()都会形成Socket关闭 可是不关闭又致使服务端没法接收到-1blog
二、使用socket.shutdownOutput();来通知服务端发送完成
三、write后记得flush
四、关闭流