java socket 长连接事例

一 .我们知道java的socket是基于TCP的连接,而ServerSocket 的accept()方法是阻塞的,直到有客户端连接到服务器端,我们常用多线程的方式来实现服务器端响应多个客户端,以下是代码:

public class server {
	public static void main(String[] args) {
		try {
			ServerSocket serverSocket = new ServerSocket(9999);
            System.out.println("服务器端--开始监听");

            while(true){
                Socket socket  = serverSocket.accept();
                ServerHandel hm = new ServerHandel(socket);
                Thread t = new Thread(hm);
                t.start();
            }

            //1.socket 的输入输出流任意一个关闭,则socket都不可再用了,所以要关闭就一起关闭了。
            //2.socket 流的读是阻塞的,A不要输入流关闭前时,要考虑B端的输出流是否还需要写。否者,B端一直等待A端接收,而A端却接受不了,B一直阻塞,在长连接中尤其要注意,注意流的结束标志
			//3.io 流最后一定要关闭,不然会一直占用内存,可能程序会崩溃。文件输出也可能没有任何信息
            //4.字符输出推荐使用printWriter 流,它也可以直接对文件操作,它有一个参数,设置为true 可以自动刷新,强制从缓冲中写出数据
            //5.缓冲流 都有 bufw.newLine();方法,添加换行
            //6.输入流 是指从什么地方读取/输出流是指输出到什么地方
            //7.OutputStreamWriter和InputStreamReader是转换流,把字节流转换成字符流
            //8.Scanner 取得输入的依据是空格符,包括空格键,Tab键和Enter键,任意一个按下就会返回下一个输入,如果需要包括空格之类的则用bufferreader来获取输入
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}


 二 这里有2个概念介绍下:

1.短连接:  当输出流发送完毕后,立即关闭流,下次发送,需要新建通信. 会频繁的新建通信,丢弃通信,效率低

2.长连接: 客户端和服务器端都不关闭流, 可以多次发送数据

 

三 长连接的使用需要注意一些问题:

1.客户端和服务器端,不能关闭任一socket的输入流或者输出流,否则socket通信会关闭

2.由于都不关闭连接,而read方法又是阻塞的,会一直读取数据,不知道何时读取结束,所以要约定通信协议,如特定字符,或者使用包头+包体的方式,传递数据,包头固定长度,里面保存包体长度等信息,这样服务端就知道读取到何时结束了.(本文使用此种方式)以下是代码:

客户端:

public class client {

   public static void main(String[] args) {

	   Socket  socket = null;

	   try {
		    socket = new Socket("127.0.0.1",9999);
            System.out.println("客户端开始连接");
            //一直读取控制台
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
           while (true){
               //包体
               byte [] content = br.readLine().getBytes();
               //包头,固定4个字节,包含包体长度信息
               byte [] head = Tool.intToByteArray1(content.length);
               BufferedOutputStream bis = new BufferedOutputStream(socket.getOutputStream());
               bis.write(head);
               bis.flush();
               bis.write(content);
               bis.flush();
           }


	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}finally {
           try {
               socket.close();
           }catch (Exception e){
               e.printStackTrace();
           }
       }

   }
}


服务器端线程类:

public class ServerHandel implements Runnable {
    public static int count = 0;
    Socket socket = null;
    public ServerHandel(Socket socket){
        count++;
        this.socket = socket;
        System.out.println("用户"+count+"接入");
    }

    @Override
    public void run() {
        BufferedInputStream bis = null;
        try {
             bis = new  BufferedInputStream(socket.getInputStream());
            while (true){
                byte [] head = new byte[4];
                bis.read(head);
                byte [] data = new byte[Tool.byteArrayToInt(head)];
                bis.read(data);
                System.out.println(new String(data).trim());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                bis.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

四  由于以字节数组传递包头,就涉及到字节数组和int类型转换的问题,java中int类型是4个字节,所以用byte[4]来装,

//int 转字节数组
    public static byte[] intToByteArray1(int i) {
        byte[] result = new byte[4];
        result[0] = (byte)((i >> 24) & 0xFF);
        result[1] = (byte)((i >> 16) & 0xFF);
        result[2] = (byte)((i >> 8) & 0xFF);
        result[3] = (byte)(i & 0xFF);
        return result;
    }

    public static byte[] intToByteArray2(int i) throws Exception {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(buf);
        out.writeInt(i);
        byte[] b = buf.toByteArray();
        out.close();
        buf.close();
        return b;
    }

    //字节数组转int
    public static int byteArrayToInt(byte[] b) {
        int intValue=0;
        for(int i=0;i<b.length;i++){
           intValue +=(b[i] & 0xFF)<<(8*(3-i));
        }
        return intValue;
    }


五 对于网络IO有一些基本的处理规则如下:
    1.减少交互的次数。比如增加缓存,合并请求。
    2.减少传输数据大小。比如压缩后传输、约定合理的数据协议。
    3.减少编码。比如提前将字符转化为字节再传输。
    4.根据应用场景选择合适的交互方式,同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。

 

六 在jdk1.4种引入了 java.nio.* 包, 大大提高了io的性能,并加入了非阻塞io模型,引入了通道和基础数据类型的buffer类型,各位看官可以去看看,这里就不说了,有空再总结.

 

 

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值