目录

  • Socket编程模型
  • 使用网络IO流传输数据

1 Socket编程模型

Socket是一个类,叫“套接字”,用来在两个程序之间传输数据。

使用Socket编程模型的一般步骤:

第一步,在服务器程序中启动监听程序,即一个ServerSocket对象,这个程序可以监听来自其他程序的连接请求,设定服务器程序的端口号作为该ServerSocket对象的构造方法的参数;

第二步,在客户端程序中创建一个Socket对象,用于与服务器程序建立通信,将服务器程序的IP地址和端口号作为这个Socket对象的构造方法的参数;

第三步,在服务器程序中调用ServerSocket对象的accept方法,接收监听到的请求,并返回一个Socket对象,这个对象将保存客户端程序的IP地址和端口号;

第四步,先后启动服务器程序和客户端程序,存在一对Socket对象,通过从Socket对象上获取到传输IO流进行数据传输。

下面使用Java语言在两个程序之间建立连接。

// AServer.java

import java.io.IOException;
import java.net.ServerSocket;

public class AServer {

    public static void main(String[] args) throws IOException {

        // 启动监听程序
        ServerSocket s = new ServerSocket(56789);

        // 接收连接请求
        s.accept();

        System.out.println("A连接成功");

    }

}

// BClient.java

import java.io.IOException;
import java.net.Socket;

public class BClient {

    public static void main(String[] args) throws IOException {

        // 在客户端程序创建Socket对象
        new Socket("127.0.0.1", 56789);

        System.out.println("B连接成功");

    }

}

先后启动A和B,运行结果

程序 AServer:
A连接成功

程序 BClient:
B连接成功

然后,给两个程序都创建Socket对象。

public static void main(String[] args) throws IOException {

        // 启动监听程序
        ServerSocket s = new ServerSocket(56789);

        System.out.println(s.getLocalPort());
        System.out.println(s.getInetAddress());
        System.out.println(s.getLocalSocketAddress());

        // 接收连接
        Socket socketA = s.accept();

        System.out.println(s.getLocalPort());
        System.out.println(s.getInetAddress());
        System.out.println(s.getLocalSocketAddress());

        System.out.println(socketA.getPort());
        System.out.println(socketA.getLocalPort());
        System.out.println(socketA.getInetAddress());
        System.out.println(socketA.getLocalAddress());
        System.out.println(socketA.getLocalSocketAddress());

    }
public static void main(String[] args) throws IOException {

        // 在客户端程序创建Socket对象
        Socket socketB = new Socket("127.0.0.1", 56789);

        System.out.println(socketB.getPort());
        System.out.println(socketB.getLocalPort());
        System.out.println(socketB.getInetAddress());
        System.out.println(socketB.getLocalAddress());
        System.out.println(socketB.getLocalSocketAddress());

    }

先启动A,运行结果

56789
0.0.0.0/0.0.0.0
0.0.0.0/0.0.0.0:56789

再启动B,运行结果

程序 AServer:
56789
0.0.0.0/0.0.0.0
0.0.0.0/0.0.0.0:56789
56789
0.0.0.0/0.0.0.0
0.0.0.0/0.0.0.0:56789
53443
56789
/127.0.0.1
/127.0.0.1
/127.0.0.1:56789

程序 BClient:
56789
53443
/127.0.0.1
/127.0.0.1
/127.0.0.1:53443

可以分析出代码执行的过程,两个程序的IP地址和端口号,以及这些方法返回了什么。


2 使用网络IO流传输数据

涉及到两个类:

InputStream,输入流,用于程序接收数据

OutputStream,输出流,用于程序发送数据

具体操作如下:

在发送端创建OutputStream对象。使用socketB的getOutputStream方法返回一个OutputStream对象。

在发送端使用OutputStream对象的write方法发送数据。将要发送的数据拆分成一个个字节,形成一个个不大于256的int作为write方法的参数,即可发送。

在接收端创建InputStream对象。使用socketB的getInputStream方法返回一个InputStream对象。

在接收端使用InputStream对象的read方法接收数据。依次接受发送方发来的一个个字节,然后拼接成一串完整的数据。

下面先演示发送一个字节的数据。

public static void main(String[] args) {

        try {

            ServerSocket s = new ServerSocket(56789);
            Socket socketA = s.accept();
            OutputStream os = socketA.getOutputStream();
            os.write(20);

            // 强制立即发送
            os.flush();

            // 关闭进程占用的资源
            os.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
public static void main(String[] args) {

        try {

            Socket socketB = new Socket("127.0.0.1",56789);
            InputStream is = socketB.getInputStream();
            int msg = is.read();
            System.out.println(msg);
            is.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
    }

先后启动A和B,运行结果

程序 BClient:
20

如果要发送大于256的整数,代码如下。(例如1234567)

public static void main(String[] args) {

        try {

            ServerSocket s = new ServerSocket(56789);
            Socket socketA = s.accept();
            OutputStream os = socketA.getOutputStream();

            // aka  10010(18) 11010110(214) 10000111(135)
            int msg = 1234567; 
            int msg1 = (msg >> 16) & 255;
            int msg2 = (msg >> 8) & 255;
            int msg3 = msg & 255;
            os.write(msg1);
            os.write(msg2);
            os.write(msg3);
            os.flush();
            os.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
public static void main(String[] args) {

        try {

            Socket socketB = new Socket("127.0.0.1", 56789);
            InputStream is = socketB.getInputStream();
            int msg1 = is.read();
            int msg2 = is.read();
            int msg3 = is.read();
            int msg = msg1 << 16 | msg2 << 8 | msg3;
            System.out.println(msg1);
            System.out.println(msg2);
            System.out.println(msg3);
            System.out.println(msg);
            is.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

先后启动A和B,运行结果

程序 BClient:
18
214
135
1234567