Java网络编程之Socket和ServerSocket详解

Socket

Socket是实现客户端套接字的类,套接字是两台计算机之间进行通信的端点。
Socket的实际工作由SocketImpl类的实例执行 。通过更改用于创建套接字实现的套接字工厂,应用程序可以配置自身以创建适合于本地防火墙的套接字。

常用API详解:
获取方法:
//创建一个未连接的套接字,需要用相应成员方法进行连接才能使用。
public Socket()

//传入一个ip和端口进行创建套接字并进行连接
public Socket(String host, int port)

//传入一个InetAddress 对象和一个端口进行套接字创建并进行连接。InetAddress 是对要连接的地址进行封装的一个类。
public Socket(InetAddress address, int port)

//创建一个未连接的套接字,不过可以自定义套接字子类,实现特定功能
Socket(SocketImpl impl)

//建立套接字,可指定绑定本地的ip和端口,和要连接到的远程的IP和端口
//localAddr localPort 指定绑定本地ip和端口
//address port  指定要连接的远程ip和端口。
public Socket(InetAddress address, int port, InetAddress localAddr,int localPort) 

//建立套接字,可指定绑定本地的ip和端口,和要连接到的远程的IP和端口
//localAddr localPort 指定绑定本地ip和端口
//host port  指定要连接的远程ip和端口。
public Socket(String host, int port, InetAddress localAddr, localPort) 
其他API:

/**
* 连接服务器套接字地址 
* SocketAddress endpoint   要连接到的服务器套接字地址的封装。
*/
public void connect(SocketAddress endpoint)

/**
* 连接服务器套接字地址 
* SocketAddress endpoint   要连接到的服务器套接字地址的封装。
* timeout 设置连接超时。
*/
public void connect(SocketAddress endpoint, int timeout)

/**
* 关闭套接字
*/
public synchronized void close()

/**
* 返回此套接字的输入流,也就是获取远程计算机连接传递过来的数据
*/
public InputStream getInputStream()

/**
* 返回此套接字的输出流,也就是此套接字要传输到远程连接到的套接字的数据流。
*/
public OutputStream getOutputStream()

//返回此套接字连接到的远程套接字的端口号,是远程套接字。
public int getPort()

//将套接字绑定到本地地址与端口 SocketAddress 是对套接字组成的抽象封装。
public void bind(SocketAddress bindpoint)

//返回套接字连接到的远程套接字的地址。
public InetAddress getInetAddress()

//获取是否空闲时像对方发送探测针用以确定对方连接是否关闭。
public boolean getKeepAlive() 

//设置是否空闲时像对方发送探测针用以确定对方连接是否关闭。这里只是个开关,具体还是要看操作系统配置。
public void setKeepAlive(boolean on)

//获取套接字绑定到的本地地址
public InetAddress getLocalAddress()

//返回此套接字绑定到的本地端口号。
public int getLocalPort()

//返回此套接字绑定到的本地的套接字地址 SocketAddress 是对套接字地址的封装。
public SocketAddress getLocalSocketAddress()

//返回此套接字连接到的远程套接字的地址,未连接返回null值。
public SocketAddress getRemoteSocketAddress();

ServerSocket

此类是实现服务器套接字。服务器套接字等待请求通过网络进入。它根据该请求执行一些操作,然后可能将结果返回给请求者。
服务器套接字的实际工作由SocketImpl该类的实例执行。应用程序可以更改创建套接字实现的套接字工厂,以将其自身配置为创建适合于本地防火墙的套接字。

常用API:
构造器
//创建一个未绑定的服务器套接字。需要通过bind方法绑定
public ServerSocket()

//创建绑定到指定端口的服务器套接字。
 public ServerSocket(int port)

//创建绑定到指定端口的服务器套接字。,并且可以指定积压连接数,也就是ServerSocket会有一个队列
//放置还没处理的socket连接,backlog就是设置该队列的容量,超出会拒绝连接。
public ServerSocket(int port, int backlog)

//创建具有指定端口的服务器,指定绑定的本地IP地址。设置积压连接数
public ServerSocket(int port, int backlog, InetAddress bindAddr) 
其他API:
//监听与此套接字建立的连接并接受它,这个方法会阻塞线程或者进程。知道有客户端连接过来,连接阻塞。
public Socket accept()

//关闭套接字。
public void close()

//将该套接字绑定到特定的本地ip和端口
public void bind(SocketAddress endpoint)

//将该套接字绑定到特定的本地ip和端口,设置积压连接数
public void bind(SocketAddress endpoint,int backlog)

//返回此服务器套接字的本地地址。
public InetAddress getInetAddress()

//返回此套接字正在监听的端口号。
public int getLocalPort() 

//返回此套接字绑定到的端点的地址。
public SocketAddress getLocalSocketAddress()

//返回该套接字是否已经绑定
public boolean isBound()

//返回该套接字是否已经关闭
public boolean isClosed()

一个用Socket ServerSocket写的Demo


//服务端的代码。
public class ServerSocketDemo {

    private static final int DEFAULT_PORT = 2510;

    public static void main(String[] args) {

        ServerSocket serverSocket = null;
        try {
            //建立服务监听,端口2510
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("端口:" + DEFAULT_PORT + "服务已启动");
            while (true){
                //监听连接,在客户端连接来临之前会处于阻塞状态。
                Socket socket = serverSocket.accept();
                //获取输入流。这个是从客户端传输过来的数据。
                InputStream inputStream = socket.getInputStream();
                //这里对输入的数据进行拆解
                byte[] readContent = DataWrapper.wrapInputStreamData(inputStream);
                System.out.println(new String(readContent));


                //获取输出流。这个是要写到客户端的数据。
                OutputStream outputStream = socket.getOutputStream();

                byte[] writeContent = "请求已经收到,谢谢!".getBytes();
                //对要写入到输出流的数据进行封装
                byte[] writeData = DataWrapper.wrapOutputStreamData(writeContent);

                //返回点信息给客户端
                outputStream.write(writeData);
                outputStream.flush();
                //关闭socket,同时会关闭该socket的输出流和输入流。
                socket.close();

            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

//客户端代码
public class SocketDemo {



    public static void main(String[] args) {

        try {
            //建立客户端连接socket,此操作完成后,要连接到的服务监听就会接收到客户端连接并处理逻辑。
            Socket socket = new Socket("127.0.0.1",2510);

            //获取输出流。这个是要写到客户端的数据。
            OutputStream outputStream = socket.getOutputStream();

            byte[] content = "你好,ServerSocketDemo,我是SocketDemo".getBytes();

            对要写入到输出流的数据进行封装
            byte[] writeByte = DataWrapper.wrapOutputStreamData(content);

            //写入数据给服务端
            outputStream.write(writeByte);
            outputStream.flush();

            //获取输入流。这个是从服务端传输过来的数据。
            InputStream inputStream = socket.getInputStream();
            //这里对输入的数据进行拆解
            byte[] readContent = DataWrapper.wrapInputStreamData(inputStream);
            System.out.println(new String(readContent));

            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}



//==============工具类

/**
 * @author YeHaocong
 * @decription 对输出流和输入流的数据进行封装和拆解,因为socket流不能像文件数据流那样判断出是否到达数据流的结尾,所以
 * @decription 要对写入到输出流的数据进行长度封装,封装在流的前四个字节里,然后处理输入流时对数据进行拆解。
 * 
 */


public class DataWrapper {

    /**
     * 对将要写到输出流数据进行封装
     * @param writeData
     * @return
     */
    public static byte[] wrapOutputStreamData(byte[] writeData){
        //得到数据的长度
        int contentLen = writeData.length;
        //将数据长度从int型转化为长度为4的字节数组。
        byte[] contentLenByte = ByteArrayUtils.intToBytes(contentLen);

        //将长度数组与数据数组合并,前面四个字节是长度数组
        byte[] writeByte = ByteArrayUtils.mereTwoByteArray(contentLenByte,writeData);
        return writeByte;
    }

    /**
     * 对输入流的数据进行包装
     * @param in
     * @return
     * @throws IOException
     */
    public static byte[] wrapInputStreamData(InputStream in) throws IOException {
        //4个长度的字节数组,用于存储输入流数据字节数组的前4个字节数据,该数据记录了输入流数据的长度。
        byte[] readContentLenBytes = new byte[4];
        in.read(readContentLenBytes);
        //将字节数组转换成相应的int类型
        int readContentLen = ByteArrayUtils.bytesToInt(readContentLenBytes);

        //存储数据的数组
        byte[] readContent = new byte[readContentLen];
        in.read(readContent,0,readContentLen);
        //返回不带前4个字节的真正的数据。
        return readContent;
    }
}

/**
 * @author YeHaocong
 * @decription 字节数组工具类
 * 
 */
public class ByteArrayUtils {

    private static ByteBuffer buffer = ByteBuffer.allocate(4);

    /**
     * int类型转字节数组
     * @param num
     * @return
     */
    public static byte[] intToBytes(int num) {
        buffer.putInt(0, num);
        return buffer.array();
    }

    /**
     * 字节数组转int类型
     * @param bytes
     * @return
     */
    public static int bytesToInt(byte[] bytes) {
        buffer.put(bytes, 0, bytes.length);
        buffer.flip();
        return buffer.getInt();
    }

    /**
     * 合并两个字节数组
     * @param byteArr1 在新数组前面
     * @param byteArr2 字新数组后面
     * @return
     */
    public static byte[] mereTwoByteArray(byte[] byteArr1,byte[] byteArr2){
        byte[] result= new byte[byteArr1.length+byteArr2.length];
        System.arraycopy(byteArr1, 0, result, 0, byteArr1.length);
        System.arraycopy(byteArr2, 0, result, byteArr1.length, byteArr2.length);
        return result;
    }


}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值