请选择 进入手机版 | 继续访问电脑版
搜索
房产
装修
汽车
婚嫁
健康
理财
旅游
美食
跳蚤
二手房
租房
招聘
二手车
教育
茶座
我要买房
买东西
装修家居
交友
职场
生活
网购
亲子
情感
龙城车友
找美食
谈婚论嫁
美女
兴趣
八卦
宠物
手机

初识Socket通讯编程(一)

[复制链接]
查看: 81|回复: 0

2万

主题

2万

帖子

7万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
70839
发表于 2020-1-17 02:47 | 显示全部楼层 |阅读模式
一、什么是socket?  当两台盘算机必要通讯的时候,凡是我们操纵的都是TCP去实现的,可是并不会间接去操纵TCP协议,凡是是经过Socket举行tcp通讯。Socket是操纵系统供给给开辟者的一个接口,经过它,便可以实现装备之间的通讯。 二、TCP是怎样通讯的?  TCP毗连和断开别离会存在3次握手/4此握手的进程,而且在此进程中包含了发送数据的长度(担任数据的长度),无容置疑,这个进程是复杂的,这里我们不必要做深入的探讨。倘使有爱好,可以参考此文章,这里具体的表白了TCP通讯的进程:https://ketao1989.github.io/2017/03/29/java-server-in-action/ 三、Socket消息的收发  在Java中处置惩罚socket的方式有三种:

  • 传统的io流方式(BIO形式),阻塞型;
  • NIO的方式;
  • AIO的方式;
  这里只先容传统的IO流方式的tcp毗连,即InputStream和OutputStream的方式读取和写入数据。对于长毗连,凡是情况大要我们以下做:
  1. //public class SocketReadLister implements Runnable {    private final int tcpPort=9999;    private ServerSocket serverSocket;    @Override    public void run() {        try {            serverSocket = new ServerSocket(this.tcpPort);            while(true){                Socket socket = serverSocket.accept();                //socket.setSoTimeout(5*1000);//设备读取数据超不时候为5s                new Thread(new SocketReadThread(socket)).start();            }        }catch (Exception e){            e.printStackTrace();        }    }    public static void main(String[] args) throws Exception{        new Thread(new SocketReadLister()).start();    }}public class SocketReadThread implements Runnable {    private Socket socket;    public SocketReadThread(Socket socket) {        this.socket = socket;    }    @Override    public void run() {        byte[] data = new byte[1024];        try {            InputStream is=socket.getInputStream();            int length=0;            int num=is.available();            while((length = is.read(data)) != -1){                String result = new String(data);                System.out.println("数据available:"+num);                System.out.println("数据:"+result);                System.out.println("length:" + length);            }            System.out.print("竣事数据读取:"+length);        }catch (SocketTimeoutException socketTimeoutException){            try {                Thread.sleep(2*1000);            }catch (Exception e) {                e.printStackTrace();            }            run();        } catch (Exception e){            e.printStackTrace();            try {                socket.close();            }catch (IOException io){                io.printStackTrace();            }        }    }}
复制代码
  1. //public class SocketClient implements Runnable {    private final int tcpPort=9999;    private Socket socket;        @Override    public void run() {        String msg = "ab23567787hdhfhhfy";        byte[] byteMsg = msg.getBytes();                    try {            socket = new Socket("127.0.0.1", 9999);            OutputStream out = socket.getOutputStream();            InputStream inputStream=socket.getInputStream();                        out.write(byteMsg);            Thread.sleep(10*1000);            char[] chars=msg.toCharArray();            String str="";            /*out.flush();*/            for(int i=0;i0) {                    if(inputStream.read(bytes)!=-1) {                        System.out.println(new String(bytes));                    }                }                Thread.sleep(10*1000);            }        } catch (Exception e) {            e.printStackTrace();            try {                socket.close();            } catch (IOException e2) {                e2.printStackTrace();            }        }    }    public static void main(String[] args) {        new Thread(new SocketClient()).start();    }}
复制代码
  正如代码中所示,凡是情况下我们在while循环中将is.read(data)) != -1作为判定根据,判定能否继续读取,这类情况下,确切可以将数据完整的读取,可是客户端没有传输数据的时候,read()方式起头阻塞,直到稀有据时才继续实行后续代码,使得步伐挂起。  为什么会出现这类情况呢?  在JDK中,关于read()的分析以下:当读取到流的末端,没有可读数据的时候,read()方式将返回-1,假如没稀有据,那末read()将会发生阻塞。是以,在读取文件流的情况下,这样是完全切确的,可是在收集编程的情况下,socket毗连不会断开,那末InputStream的read()将永久不会返回-1,步伐将读完数据后,继续循环读取然后发生阻塞。  在InputStream中,供给了available();此方式黑白阻塞的,经过它可以开真个判定socket流中能否稀有据,并返回一个预估数据长度的值,可是请留意,这里是预估,并不是正确的盘算出数据的长度,所以在JDK分析文档中,有提醒操纵该方式获得的值去声明 byte[]的长度,然后读取数据,这是毛病的做法。这样在每次读取数据之前,都可以先判定一下流中能否存在数据,然后再读取,这样便可以制止阻塞形成步伐的挂起。代码以下:
  1. while(true){    if(is.available()>0){        is.read(data);    }}
复制代码
  说到read(),在InputStream中供给了3个read的重载方式:read()、read(byte[])、read(byte[],int offset,int len);背面两种读取方式都是基于 read()实现的,一样存在阻塞的特征,那末我们可以思考一下,假定byte[]的长度为1024,撇开while,拿read(byte[])一次性读取来说,当另一端发送的数据不够1024个字节时,为什么这个read(byte[])没有发生阻塞?  关于这个题目,网上有帖子说,这跟InputStream的flush()有关,但经过测试,我不这么以为。我加倍认同https://ketao1989.github.io/2017/03/29/java-server-in-action/中所说的那样,TCP握手时代,会传递数据的长度,当读取完数据,read()返回-1,即使此时没有读取到1024个字节数据,剩下的用0加添,这样就能很好的表白这个题目了。  Socket既然时收集通讯用,那末由于各类原因原由,一定会有收集迟误,形成socket读取超时;socket读取超不时,其毗连任然是有用的,是以在处置惩罚该很是时不必要封闭毗连。以下是代码片断:
  1. if (nRecv < nRecvNeed){    int nSize = 0;    wsaBuf=new byte[nRecvNeed-nRecv];    int readCount = 0; // 已经乐成读取的字节的个数    try {        while (readCount < wsaBuf.length) {            //Thread.sleep(100);//读取之前先将线程休眠,制止循环时,步伐占用CPU太高            try {                availableNum=inputStream.available();                if(availableNum>0){                    readCount += inputStream.read(wsaBuf, readCount, (wsaBuf.length - readCount));//制止数据读取不完整                }            }catch (SocketTimeoutException timeOut){                System.out.println("读取超时,线程实行休眠操纵,2秒后再读取");                Thread.sleep(2*1000);            }        }    }catch (Exception e){        System.out.println("读取数据很是");        e.printStackTrace();        close();//封闭socket毗连        break;    }    nSize=wsaBuf.length;    nRecv+=nSize;}
复制代码
  此外,必要补充实析的是,socket.close()方式实行后,只能变动本真个毗连状态,不能将该状态看护给对端,也就是说假如办事端或客户端一方实行了close(),另一端并不晓得此时毗连已经断开了。  此外,以上代码还存在一个很严重的题目亟待打点,这也是在开辟中轻易轻忽的地方——步伐能一般运转,但CPU占用太高;原因原由以下:  当readCount < wsaBuf.length,即数据还未读取完整时,线程会连续不停的从socket流中读取数据,由于这里操纵了inputStream.available()来判定操纵必要读取数据,当没稀有据传输的时候,此处就酿成了一个死循环,说到此处,原因原由就很是了然了,在盘算机运转进程中不管他是单核还是多核,系统获得盘算机资本(CPU等)都是依照时候分片的方式举行的,同一时候有且只要一个线程能获得到系统资本,所以当碰到死循环时,系统资本一向得不到开释,是以CPU会越来越高,打点的法子是在循环中对步伐举行线程休眠肯按时候。
免责声明:假如加害了您的权益,请联系站长,我们会实时删除侵权内容,感谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2006-2014 淄博新闻网-淄博日报 淄博晚报 淄博财经新报 掌中淄博 淄博专业新闻资讯发布网站 版权所有 法律顾问:高律师 客服电话:0791-88289918
技术支持:迪恩网络科技公司  Powered by Discuz! X3.2
快速回复 返回顶部 返回列表