java网络编程之Socket编程

概念

网络编程分为BIO(传统IO)、NIO、AIO。Socket编程属于BIO这种传统IO。java

InetAddress

  java.net.InetAddress是JAVA中管理IP地址的类,经常使用编程

  

  

   public static void main(String[] args) throws UnknownHostException {
        InetAdressDemo.getLocalHost();
        System.out.println("---------------------------");
        getHostByName("Lenovo-Autumn");
    }    /**
     * 获取主机ip和主机名
     * @throws UnknownHostException     */
    public static void getLocalHost() throws UnknownHostException {        //根据InetAddress获取主机名和主机ip
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);   //打印:Lenovo-Autumn/192.168.56.1        //根据getLocalHost()返回的值获取ip和主机名
        String hostName = localHost.getHostName();
        String hostAddress = localHost.getHostAddress();
        System.out.println(hostName); //打印 Lenovo-Autumn
        System.out.println(hostAddress); //打印 192.168.56.1        //根据切割获取主机名和ip
        String[] str = localHost.toString().split("/");
        System.out.println(str[0]); //打印 Lenovo-Autumn
        System.out.println(str[1]); //打印 192.168.56.1    }    /**
     * 根据主机名称获取ip地址
     * @param otherName  主机名(能够是局域网中的机器名或者是域名或者是ip)
     * @throws UnknownHostException     */
    public static void getHostByName(String otherName) throws UnknownHostException {
        InetAddress otherHost = InetAddress.getByName(otherName);
        String hostName = otherHost.getHostName();
        String hostAddress = otherHost.getHostAddress();
        System.out.println(hostName); //打印 Lenovo-Autumn
        System.out.println(hostAddress); //打印 192.168.56.1
        System.out.println(otherHost);   //打印:Lenovo-Autumn/192.168.56.1
    }

  code数组

UDP

  发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址同样。服务器

  接收端不须要明确知道数据的来源,只须要接收到数据便可。网络

  java.net.DatagramPackage

构造函数:并发

第一种是用来接受的数据包,不须要指定IP和端口,第二种是用来发送的数据包须要指定ip和端口异步

方法:socket

获取ip地址,获取服务器ip或者客户端ipide

  

  返回端口号,获取发送方或者函数

  返回数据缓冲区

  

  返回要发送或接受的数据包大小

  java.net.DatagramSocket

  DatagramPacket数据包的做用就如同是“集装箱”,能够将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还须要有码头。在程序中须要实现通讯只有DatagramPacket数据包也一样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的做用就相似于码头,使用这个类的实例对象就能够发送和接收DatagramPacket数据包,发送数据的过程以下图所示。

构造函数:

用来建立发送端的DatagramSocket对象

用来建立接收端的DatagramSocket对象

  方法:

发送数据报包

接收数据报包

发送端

1,  建立DatagramSocket对象

DatagramSocket()

2,建立DatagramPacket对象,封装数据,并指定ip和端口。

    DatagramPacket(byte[] buf, int length, InetAddress address, int port)  

3,发送数据

    socket.send(DatagramPacket dp)

4,释放流资源

ds.close();

接收端

1,建立DatagramSocket对象,只须要指定端口

    DatagramSocket(port)

2,建立DatagramPacket对象

    DatagramPacket(byte[] data, int length)

3,接收数据存储到DatagramPacket对象中 

    receive(DatagramPackage dp)

4,获取DatagramPacket对象的内容   

    new String(data,0,dp.getLength());

5,释放流资源

    ds.close();

/**
 * 实现udp发送端
 *  用java.net.DatagramPackage封装数据
 *  用java.net.DatagramSocket发送数据
 *
 * 实现步骤
 *  1.用DatagramPackage对象,封装数据,接受的地址和端口
 *  2.建立DatagramSocket
 *  3.调用DatagramSocket对象send方法,发送数据
 *  4.关闭资源
 *
 *  DatagramPackage构造函数
 *    DatagramPacket(byte[] buf, int length, InetAddress address, int port)
 *  DatagramSocket构造函数
 *    DatagramSocket()
 *    方法:send(DatagramPacket d)
 *  Created by Autumn on 2018/2/5. */public class UdpSend {    public static void main(String[] args) throws Exception {
        Scanner scanner = new Scanner(System.in);        //获取地址
        InetAddress inet = InetAddress.getByName("127.0.0.1");        //建立DatagramSocket,负责接受和发送数据
        DatagramSocket ds = new DatagramSocket();        while(true){
            String msg = scanner.nextLine();            //建立数据包对象对象
            byte[] data = msg.getBytes();            //封装数据,接受的地址和端口
            DatagramPacket dp = new DatagramPacket(data,data.length,inet,6000);            //发送数据包            ds.send(dp);            if(msg.equals("exit")){                break;
            }
        }        //关闭        ds.close();
    }
}/**
 *  实现udp接收端
 *  用java.net.DatagramPackage 接受数据
 *  用java.net.DatagramSocket 接受数据包
 *
 *  步骤
 *  1.建立DatagramSocket对象,绑定端口号(要和发送端端口一致)
 *  2.建立字节数组用来接受数据
 *  3.建立数据对象包DatagramPackage
 *  4.建立DatagramSocket
 *    receive(DatagramPackage dp)接受数据,将数据封装如dp中
 *  5.拆包
 *    发送端的ip地址(DatagramPackage.get)
 *    接受到的字节数组
 *    发送的端口号
 *  6.关闭资源
 * Created by Autumn on 2018/2/5. */public class UdpReceive {    public static void main(String[] args) throws IOException {        //建立数据包传输的对象,并绑定端口号
        DatagramSocket ds = new DatagramSocket(6000);        //建立字节数组
        byte[] data = new byte[1024];        while(true){            //建立数据包对象,传递字节数组
            DatagramPacket dp = new DatagramPacket(data,data.length);            //调用ds对象的receive接受数据包,receive()有线程阻塞效果会一直等待接受数据            ds.receive(dp);            //获取数据包大小
            int len = dp.getLength();            //获取发送端的ip地址
            InetAddress sendAddress = dp.getAddress();
            String sendHostAddress = sendAddress.getHostAddress();            //System.out.println(sendHostAddress);            //获取发送端端口号
            int port = dp.getPort();            //System.out.println(port);            //System.out.println(new String(data));   //直接打印1024个字节的字符串,有不少空格
            System.out.println(sendHostAddress+":"+port+"    "+new String(data,0,len));   //这样打印没有多余的空格

            if(new String(data,0,len).equals("exit")){                break;
            }
        }        //关闭        ds.close();
    }
}

  code

TCP

ServerSocket类,用于表示服务器端,Socket类,用于表示客户端。创建链接后用流进行输入和输出

 

ServerSocket

   实例化一个ServerSocket类,指定端口号

  监听并接受此套接字的链接

  返回此服务器套接字的本地地址

  Socket

  实例化一个Socket并指定ip和端口

  

返回一个输出流,用于客户端发送数据

  返回一个输入流,用于服务器端接受数据

  

  返回ip地址(服务器端的地址)

  返回端口号

客户端

1,建立客户端的Socket对象,指定服务器IP和端口号

  Socket(String host, int port)      

2,获取Socket的输出流对象

  getOutputStream(); 

3,写数据给服务器

  out.write("服务器数据".getBytes());

4,关闭流资源

  socket.close();

服务器端

1,建立服务器端ServerSocket对象,指定服务器端端口号

  ServerSocket(int port)

2,开启服务器,等待着客户端Socket对象的链接,若有客户端链接,返回客户端的Socket对象

  accept()

3,经过客户端的Socket对象,获取客户端的输入流,为了实现获取客户端发来的数据

  socket.getInputStream();

4,经过客户端的输入流,获取流中的数据

byte[] data = new byte[1024];
int len = inputStream.read(data);
System.out.println(new String(data,0,len));

5,经过客户端的Socket对象,获取客户端的输出流,为了实现给客户端反馈信息

6,经过客户端的输出流,写数据到流中

7,关闭流资源

socket.close();
serverSocket.close();

/**
 * 实现TCP服务器程序
 * 表示服务器程序的类java.net.ServerSocket
 * 构造方法:
 *      ServerSocket(int port);   传递端口号
 *
 * Important:必须得到客户端的套接字(Socket)
 *     方法:Socket accept()
 *     服务器能够获取到客户端的套接字
 * Created by Autumn on 2018/2/5. */public class TCPServer {    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);        //调用服务器套接字对象accept()获取客户端套接字,具备线程等待效果
        Socket socket = serverSocket.accept();    //这里会阻塞等待链接接入    cmd中telnet 127.0.0.1 8888便可链接        //根据得到的客户端的socket获取输入流
        InputStream inputStream = socket.getInputStream();        //根据输入流将数据读入到data中
        byte[] data = new byte[1024];        int len = inputStream.read(data);   //这里会阻塞,等待数据   cmd中ctrl+]进入到telnet操做模式send value发送数据
        System.out.println(new String(data,0,len));
        socket.close();
        serverSocket.close();
    }
}/**
 * 实现TCP客户端,链接到服务器
 * 和服务器实现数据交换
 * 实现TCP客户端程序的类 java.net.Socket
 *
 * 构造方法:
 *      Socket(String host, int port)传递服务器IP和端口号
 *      注意:构造方法只要运行,就会和服务器进行链接,链接时报,抛出异常
 *
 *      OutputStream    getOutputStream()  返回套接字的输出流
 *          做用:将数据输出,输出到服务器
 *      InputStream    getInputStream()  返回套接字的输入流
 *          做用:从服务器端读取数据
 *
 *      客户端服务器数据交换,必须使用套接字对象Socket中的获取的IO刘,本身new的流不行
 *
 * Created by Autumn on 2018/2/5. */public class TCPClient {    public static void main(String[] args) throws IOException {        //建立Socket对象,链接服务器
        Socket socket = new Socket("127.0.0.1",8888);        //经过客户端的套接字对象Socket方法,获取字节输出流,将数据写向服务器
        OutputStream out = socket.getOutputStream();
        out.write("这是一条来客户端的数据".getBytes());
        socket.close();
    }
}

用cmd命令telnet实现和SocketServer互动

import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class TCPServer {    /*同步阻塞*/
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端启动成功...");        while(true){            /*一次只能处理一个链接,在一个链接没关闭前没法接收第二个链接,在这里开一个框发送数无问题,开放两个框时会出现第二个无反应*/
            Socket socket = serverSocket.accept();   //这里会阻塞等待链接接入    cmd中telnet 127.0.0.1 8888即表明链接
            System.out.println("新客户端链接成功....");
            InputStream inputStream = socket.getInputStream();            while(true) {                byte[] data = new byte[1024];
                System.out.println("正在等待数据...");                int len = inputStream.read(data);    //这里会阻塞,等待数据,若是直接关闭cmd窗口会由于关闭socket通道致使len返回-1   cmd中ctrl+]进入到telnet操做模式send value发送数据
                if (len != -1){
                    System.out.println(new String(data, 0,len, "GBK"));   //用GBK是由于CMD窗口命令发送的数据是GBK编码
                }else{                    break;
                }
            }
        }        //socket.close();        //serverSocket.close();        //System.out.println("服务器端关闭....");    }
}

链接服务端 

向服务端发送数据

再开一个cmd而后telnet发送数据,发现无反应。必须关闭第一个cmd才能链接成功。

线程池改进,能并发访问

用ExecutorService线程池实现每个链接建立一个新的线程

import java.io.IOException;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TCPServer {    /*异步阻塞*/
    public static void main(String[] args) throws IOException {
        ExecutorService threadPool = Executors.newCachedThreadPool();   //线程池
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端启动成功...");        while(true){            /*一次只能处理一个链接,在一个链接没关闭前没法接收第二个链接,在这里开一个框发送数无问题,开放两个框时会出现第二个无反应*/
            System.out.println("等待客户端链接...");            final Socket socket = serverSocket.accept();   //这里会阻塞等待链接接入    cmd中telnet 127.0.0.1 8888即表明链接
            threadPool.execute(new Runnable() {   //启动一个线程
                public void run() {                    try {
                        System.out.println("新客户端链接成功....");
                        InputStream inputStream = socket.getInputStream();                        while(true) {                            byte[] data = new byte[1024];
                            System.out.println("正在等待数据...");                            int len = inputStream.read(data);    //这里会阻塞,等待数据,若是直接关闭cmd窗口会由于关闭socket通道致使len返回-1   cmd中ctrl+]进入到telnet操做模式send value发送数据
                            if (len != -1){
                                    System.out.println(Thread.currentThread()+new String(data, 0,len, "GBK"));   //用GBK是由于CMD窗口命令发送的数据是GBK编码
                            }else{
                                System.out.println("break循环");                                break;
                            }
                        }
                        System.out.println("一次Socket链接关闭");
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });

        }        //socket.close();        //serverSocket.close();        //System.out.println("服务器端关闭....");    }
}

优势:传输质量好(因此BIO适合传输链接少可是数据量大的请求)

缺点:每个链接都占用一个线程,很占用系统资源。

tip:全部的调优都关联到系统资源(IO、存储、内存、CPU)

code

相关文章
相关标签/搜索