美文网首页
网络编程系列第(三)篇---Socket

网络编程系列第(三)篇---Socket

作者: sofarsogoo_932d | 来源:发表于2018-03-27 14:00 被阅读0次

Socket是什么

Socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

----------------以上内容来自百度百科-------------------

使用TCP

服务端代码

public class TCPServer {

  // 启动服务
  public void start() {
    try {
        // 1.创建一个服务端的socket
        ServerSocket serverSocket = new ServerSocket(12345);
        System.out.println("服务器等待与客户端的连接...");
        int count=0;
        while (true) {
            // 2.调用accept方法开始监听,等待客户端的连接
            Socket client = serverSocket.accept();
            count++;
            System.out.println("客户端请求次数:"+count);
            new reponseClient(client);
        }

    } catch (IOException e) {
        System.out.println("服务器异常:" + e.getMessage());
    }

}

// 响应客户端
private class reponseClient extends Thread {

    private Socket clinet;

    public reponseClient(Socket client) {
        this.clinet = client;
        start();
    }

    public void run() {
        try {
            // 3.获取输入流,并读取客户端信息
            InputStream is = clinet.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);

            String info = null;
            while ((info = br.readLine()) != null) {
                System.out.println("我是服务器,客户端说:" + info);
            }
            clinet.shutdownInput(); // 关闭输入流

            // 4.获取输出流,响应客户端的请求
            OutputStream os = clinet.getOutputStream();
            PrintWriter pw = new PrintWriter(os);
            pw.write("欢迎你登录");
            pw.flush();
            clinet.shutdownOutput(); // 关闭输出流

            // 释放资源
            br.close();
            isr.close();
            is.close();

            pw.close();
            os.close();

        } catch (IOException e) {
            System.out.println("服务器 run 异常:" + e.getMessage());
        } finally {
            if (clinet != null) {
                try {
                    clinet.close();
                } catch (IOException e) {
                    System.out.println("服务器 finally 异常:" + e.getMessage());
                }
            }
        }

    }

}

public static void main(String[] args) {
    System.out.println("***启动服务器***");
    TCPServer server = new TCPServer();
    server.start();
  }
}

客户端代码

public class TCPClient {

  public static void main(String[] args) {
    
    System.out.println("**客户端启动**");

    try {
        // 1.创建客户端Socket,指定服务器地址和端口
        Socket socket = new Socket("localhost", 12345);

        // 2.获取输出流,向服务端发送信息
        OutputStream os = socket.getOutputStream();
        PrintWriter pw = new PrintWriter(os);
        pw.write("我是tom,我要登录");
        pw.flush();
        socket.shutdownOutput(); // 关闭输出流

        // 3.获取输入流,并读取服务端信息
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String info = null;
        while ((info = br.readLine()) != null) {
            System.out.println("我是客户端,服务端说:" + info);
        }
        socket.shutdownInput(); // 关闭输入流

        // 4.关系相关资源
        br.close();
        isr.close();
        is.close();
        pw.close();
        os.close();
        socket.close();
    } catch (Exception e) {
        System.out.println("客户端异常:"+e.getMessage());
    } 
  }
}

先启动server端的代码,然后启动3次客户端代码

客户端控制台如下

**客户端启动**
我是客户端,服务端说:欢迎你登录

服务端控制台如下

***启动服务器***
服务器等待与客户端的连接...
客户端请求次数:1
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:2
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:3
我是服务器,客户端说:我是tom,我要登录

使用UDP

服务端代码

public class UDPServer {

  public void start() {
    try {
        // 1.创建服务端DatagramSocket
        DatagramSocket socket = new DatagramSocket(4321);

        // 2.创建数据报,用于接收客户端发送的数据
        byte[] data = new byte[1024]; // 指定接收的数据包的大小
        DatagramPacket packet = new DatagramPacket(data, data.length);

        System.out.println("服务器等待与客户端的连接...");
        int count = 0;
        while (true) {
            socket.receive(packet); // 此方法在接收到数据之前,处于阻塞状态
            count++;
            System.out.println("客户端请求次数:" + count);
            new reponseClient(socket, packet);    //放在子线程有问题
        }

    } catch (Exception e) {
        System.out.println("服务器异常:" + e.getMessage());
    }

}

// 响应客户端
private class reponseClient  {

    private DatagramSocket server;
    private DatagramPacket packet;
    public reponseClient(DatagramSocket server, DatagramPacket packet) {
        this.server = server;
        this.packet = packet;
        run();
    }

    public void run() {
        try {
            // 3.读取客户端信息
            String info = new String(packet.getData(), 0, packet.getLength(), "utf-8");
            System.out.println("我是服务器,客户端说:" + info);

            // 4.响应客户端的请求
            InetAddress address = packet.getAddress();
            int port = packet.getPort();
            String s = "欢迎你登录";
            byte[] data2 = s.getBytes("utf-8");
            // 2.创建数据报,包含响应信息
            DatagramPacket packet2 = new DatagramPacket(data2,
                    data2.length, address, port);
            // 3.响应客户端
            server.send(packet2);

        } catch (IOException e) {
            System.out.println("服务器 run 异常:" + e.getMessage());
        } finally {
            
        }

    }

}

public static void main(String[] args) {
    System.out.println("***启动服务器-UDP***");
    UDPServer server = new UDPServer();
    server.start();
  }
}

客户端代码

public class UDPClient {

  public static void main(String[] args) {
    
    System.out.println("**客户端启动-UDP**");

    try {
        //1.创建DatagramSocket对象
        DatagramSocket client=new DatagramSocket();
        
        //2.定义服务器的地址,端口号,数据
        InetAddress address=InetAddress.getByName("localhost");
        int port=4321;
        String s="我是tom,我要登录";
        byte[] data=s.getBytes("utf-8");
        
        //3.创建数据报,包含发送的数据信息
        DatagramPacket packet=new DatagramPacket(data, data.length, address, port);
    
        //4.向服务器发送数据报
        client.send(packet);
        
        

        //5.创建数据报,用于接收服务端的响应信息
        byte[] data2=new byte[1024];
        DatagramPacket packet2=new DatagramPacket(data2, data2.length);
        //6.接收服务端的响应数据
        client.receive(packet2);
        //7.读取数据
        String reply=new String(packet2.getData(),0,packet2.getLength(),"utf-8");
        System.out.println("我是客户端,服务器说:"+reply);
        //8.关闭相关资源
        client.close();
    } catch (Exception e) {
        System.out.println("客户端异常:"+e.getMessage());
    }
  }
}

同样先启动server端的代码,然后启动3次客户端代码

客户端控制台如下

**客户端启动-UDP**
我是客户端,服务器说:欢迎你登录

服务端控制台如下

***启动服务器-UDP***
服务器等待与客户端的连接...
客户端请求次数:1
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:2
我是服务器,客户端说:我是tom,我要登录
客户端请求次数:3
我是服务器,客户端说:我是tom,我要登录

注意

  1. 在UDPServer代码中,启动服务放在主线程,响应客户端的代码放在子线程,第一次运行UDPClient的的时候,在服务端拿不到客户端的值。
  2. 在UDPServer代码中,启动服务放在主线程,响应客户端的代码放在主线程,一切正常。
  3. 在UDPServer代码中,启动服务放在子线程1,响应客户端的代码放在子线程2,第一次运行UDPClient的的时候,在服务端拿不到客户端的值。
  4. 在UDPServer代码中,启动服务放在子线程1,响应客户端的代码放在子线程1,一切正常。
    但在TCPServer代码中,启动服务放在主线程,响应客户端的代码放在子线程中仍然可以拿到值。

结论,UDP相关的代码一定要放在相同的线程,要么整个在主线程,要么整个在相同的子线程。

TCP在创建soket时,要求服务端和客户端的端口号要一样。
服务端的socket是ServerSocket类,客户端的socket是Socket类

UDP在创建socket时,要求服务端和客户端的端口号不一样,否则会抛地址已被占用的异常。
服务端和客户端的socket都是DatagramSocket

相关文章

网友评论

      本文标题:网络编程系列第(三)篇---Socket

      本文链接:https://www.haomeiwen.com/subject/juetpxtx.html