Socket网络编程

Socket(套接字)开发网络应用程序

两端都要有Socket,网络通信实质是指Socket间的通信

Socket允许把网络连接当作一个流,数据在两个Socket间以IO传输

分为客户端和服务端

读写数据时需要用到两个方法:

socket.getOutputStream()
socket.getInputStream()

Socket分为两类:TCP网络编程,UDP网络编程

TCP网络编程

netstat 指令

netstat -an:查看当前主机的网络情况,包括端口监听网络连接情况

netstat -an | more 分页显示

netstat -anb | more 分页显示那个程序在监听对应端口(管理员状态)

TCP网络编程案例

案例1:客户端发送消息,服务端接收来自客户端的消息,并且打印出收到的消息。

//服务端
public class SocketTCP01Server {
    public static void main(String[] args) throws IOException {
        //1,服务器端开始监听9999端口,如果本机有其他服务监听次端口,那么会抛出异常
        ServerSocket serverSocket=new ServerSocket(9999);
        System.out.println("服务端正在监听9999端口。。。");
        //2,当没有客户端连接本机的9999端口时,程序会阻塞
        //如果有客户端连接,则会返回Socket对象,程序继续
        //serverSocket可以寄接收来自多个客户端的消息
        Socket socket=serverSocket.accept();
        System.out.println("服务端socket="+socket.getClass());
        InputStream inputStream = socket.getInputStream();
        //4,IO读取
        byte[] buf=new byte[1024];
        int len=0;
        while ((len=inputStream.read(buf))!=-1){
            System.out.println(new String(buf,0,len));
            System.out.println("服务端接收数据成功。。。");
        }
        //5,关闭资源
        inputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端关闭成功。。。");
    }
}
//客户端
public class SocketTCP01Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端socket返回"+socket.getClass());
        //2得到和socket关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3通过输出流,项数据通道中输入数据
        outputStream.write("hello server".getBytes());
        //关闭输出流和socket,解决资源
        outputStream.close();
        socket.close();
        System.out.println("客户端已经退出。。。。");
    }
}

先启动服务端,当没有客户端来连接时,服务端会陷入阻塞状态

我们再启动客户端。

此时我们已经将客户端要发送的数据发送到服务端,我们再看看服务端接收到了什么数据。

这个最简单的案例这里就结束了,接下来我们改进一下程序,使得服务端接收到客户端发送的数据后,回复一条表示已经收到信息的话。

案例二:

//服务端
public class SocketTCP02Server {
    public static void main(String[] args) throws IOException {
        //1,服务器端开始监听9999端口,如果本机有其他服务监听次端口,那么会抛出异常
        ServerSocket serverSocket=new ServerSocket(9999);
        System.out.println("服务端正在监听9999端口。。。");
        //2,当没有客户端连接本机的9999端口时,程序会阻塞
        //如果有客户端连接,则会返回Socket对象,程序继续
        //serversocket可以寄接收来自多个客户端的消息
        Socket socket=serverSocket.accept();
        System.out.println("服务端socket="+socket.getClass());
        InputStream inputStream = socket.getInputStream();
        //4,IO读取
        byte[] buf=new byte[1024];
        int len=0;
        while ((len=inputStream.read(buf))!=-1){
            System.out.println(new String(buf,0,len));

        }
        System.out.println("服务端接收数据成功。。。");

        //5.服务端接收到客户端的信息后,向客户端发送一条信息。
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello client".getBytes());
        socket.shutdownOutput();
        //6,关闭资源
        outputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端关闭成功。。。");
    }
}
//客户端
public class SocketTCP02Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端socket返回"+socket.getClass());
        //2得到和socket关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3通过输出流,项数据通道中输入数据
        outputStream.write("hello server".getBytes());
        //写入结束标志,否则服务端会阻塞在while读取的过程中。
        socket.shutdownOutput();

        //4.获取与socket关联的输入流,读取服务端发送过来的数据
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len=inputStream.read(bytes) )!= -1){
            System.out.println(new String(bytes,0,len));
        }
        //5,关闭输出流和socket,解决资源
        outputStream.close();
        socket.close();
        System.out.println("客户端已经退出。。。。");
    }
}

注意:当客户端向输出流中写入数据后,必须再后面添加一个socket.shutdownOutput();它的作用是告诉服务端的输出流中不会再写入任何数据了,不用再读取了。

如果不添加这条语句的话,那么服务端会再while循环中不断的读取,却永远读不到-1结束标志,服务端也就陷入了阻塞。

同时客户端继续向下执行,因为接下来它要接收服务端回复的消息,但因为服务端已经陷入了阻塞,因此客户端中的while循环也不能脱离,也陷入了阻塞。

因此此时服务端和客户端都陷入阻塞,都等待着对方的消息。


这时,可能又有一个新的疑问产生了:那为什么案例1中客户端发送完数据后,也没有写socket.shutdownOutput();为什么它就没有陷入阻塞呢?

这时因为客户端1接下来的语句中没有while循环,直接关闭了流还有socket,它起的作用和socket.shutdownOutput() 类似于告诉对面端:我想写的东西已经写完了,你不要再读了!


UDP



UDP没有明确的服务端和客户端,只有发送端和接收端

发送数据和接收数据据都是通过DatagramSocket来完成的

将数据封装成DatagramPacket 对象发送( 装包)

接收数据需要拆包

DatagramSocke可以指定在哪一个端口接收

基本流程

1,两个核心类DatagramPacket和DatagramSocket

2,建立发送端和接收端

3,发送数据前建立数据包DatagramPacket

4,调用DatagramSocket的发送接收方法

5,关闭DatagramSocket

案例1

public class UDPSender {
    public static void main(String[] args) throws IOException {
        //创建一个DatagramSocket对象,准备在9998端口接收数据
        DatagramSocket datagramSocket = new DatagramSocket(9998);
        //2,将需要发送的数据发送到DatagramSocket对象中
        String data = "Hello UDP,明天吃火锅";
        //3,封装的对象包括字节数组,数组长度,端口等信息
        DatagramPacket datagramPacket = new DatagramPacket(data.getBytes(), data.getBytes().length, InetAddress.getByName("192.168.74.1"), 9999);
       //4,发送数据
        datagramSocket.send(datagramPacket);
        //=====接收A端回复的消息===

        //2,创建DatagramPacket接收数据,一个DatagramPacket最大存64K数据,不适合传大量数据
         datagramPacket=new DatagramPacket(new byte[1024],1024);
        //3,调用接收方法,如果没有数据,会阻塞
        System.out.println("等待接收数据。。。。");
        datagramSocket.receive(datagramPacket);
        //4,对数据包进行拆包,取出数据并显示
        int length=datagramPacket.getLength();//实际收到的数据字节长度
        byte[] data1=datagramPacket.getData();//实际接收到的数据
        String msg=new String(data1,0,length);
        System.out.println("接收到的数据是:"+msg);

        //关闭资源
        datagramSocket.close();
        System.out.println("B端退出");
    }
}
//接收数据
public class UDPReceiver {
    public static void main(String[] args) throws IOException {
        //1,创建一个DatagramSocket对象,准备在9999端口接收数据据
        DatagramSocket datagramSocket=new DatagramSocket(9999);
        //2,创建DatagramPacket接收数据,一个DatagramPacket最大存64K数据,不适合传大量数据
        DatagramPacket datagramPacket=new DatagramPacket(new byte[1024],1024);
        //3,调用接收方法,如果没有数据,会阻塞
        System.out.println("等待接收数据。。。。");
        datagramSocket.receive(datagramPacket);
        //4,对数据包进行拆包,取出数据并显示
        int length=datagramPacket.getLength();//实际收到的数据字节长度
        byte[] data=datagramPacket.getData();//实际接收到的数据
        String msg=new String(data,0,length);
        System.out.println("接收到的数据是:"+msg);
        //====回复B端消息
        //2,将需要发送的数据发送到DatagramSocket对象中
        String data1 = "Hello UDP,我收到了B端传递过来的消息";
        //3,封装的对象包括字节数组,数组长度,端口等信息
         datagramPacket = new DatagramPacket(data1.getBytes(), data1.getBytes().length, InetAddress.getByName("192.168.74.1"), 9998);
        //4,发送数据
        datagramSocket.send(datagramPacket);
        //释放资源
        datagramSocket.close();
        System.out.println("A端结束。。");
    }
}
原文链接:,转发请注明来源!