先上一张网络聊天程序运行图:

功能实现的很简单,客户端输入服务器IP和端口信息连接服务器,建立连接之后,客户端和服务器就可以进行双向通信了。

源码如下:

client端 源代码

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

public class Client extends Netutil  implements Runnable{
    String IPAdress;
    int port;

    Client(String IPAdress,int port){
        this.IPAdress=IPAdress;
        this.port=port;
    }

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入IP地址:");
        String ip=scanner.nextLine();
        System.out.print("请输入端口号:");
        int port=scanner.nextInt();

        Client client1=new Client(ip,port);

        Thread thread1=new Thread(client1);

        thread1.start();
    }

    @Override
    public void run() {
        try {

            Socket s = new Socket(this.IPAdress, this.port);
            super.Oprate(s);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Server端源代码:

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class Server extends Netutil {

    public static void main(String[] args) {
        new Server().Get();
    }

    /*进入的方法*/
    public void Get() {
        try {
            ServerSocket serverSocket = new ServerSocket(4432);
            InetAddress inetAddress = InetAddress.getLocalHost();

            // System.out.println("开启服务器");
            System.out.println("地址:" + inetAddress.getHostAddress() + "端口:" + 4432);
            Socket accept;
            while (true) {
                accept = serverSocket.accept();
                //System.out.println("主机"+accept.getRemoteSocketAddress()+"连接服务器");

                Thread hander = new Thread(new Handler(accept));
                hander.start();


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


    /**
     * 内部处理类
     */
    class Handler extends Netutil implements Runnable {
        Socket socket;

        public Handler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                super.Oprate(socket);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}

聊天功能实现类:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class Netutil {

    public void Oprate(Socket s) throws IOException {
        DataInputStream dataInputStream = new DataInputStream(s.getInputStream());
        DataOutputStream dataOutputStream = new DataOutputStream(s.getOutputStream());

        new Thread() {
            @Override
            public void run() {
                String line;
                String content = "";
                while (true) {
                    try {
                        Thread.sleep(100);
                        content = "";
                        while (!((line = dataInputStream.readUTF()).equals("EOF"))) {
                            content = content + line;
                            // System.out.println(line);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    System.out.println("收到:" + content);
                }
            }
        }.start();


        Scanner scanner = new Scanner(System.in);
        String message;

        while (true) {
            message = scanner.nextLine();
            if (message.equals("#qiut")) break;
            dataOutputStream.writeUTF(message);
            dataOutputStream.writeUTF("EOF");
            dataOutputStream.flush();
        }
        dataInputStream.close();
        dataOutputStream.close();
        s.close();
    }


}

聊天程序的流程图如下:

但是,光会用JAVA编写聊天程序显然是不行的,让我们深入其API探讨一下socket的建立过程。

在程序中,客户端初始化了一个socket连接,用来连接服务器,

调试进入之后,调用的是如下构造方法

上图中的构造方法在参数处理之后,调用了自身的其他构造,其中,下图的构造方法中,address为服务器端口地址,localAddr为自身端口地址,stream为是否要打开传输流。

在构造函数中,首先调用的是setImpl( )方法,调试跟踪发现,impl采用的是

Impl=new SocksSocketimpl()构造的,impl是SocketImpl类型,实现了SocketOptions接口,用来描述socket的参数。

再往下,看看SocksSocketImpl是如何构造的。

SocksSocketImpl构造函数为空方法,但是他继承了PlainSocketImpl类,在看看PlainSocketimpl类的构造方法,其中构造了DualStackPlainSocketImpl对象,DualStackPlainSocketImpl对象表示双向栈的Socket对象,为带有双向流socket.

DualStackPlainSocketImpl构造函数如下:

在看完了setImpl()函数调用的,过程之后,继续往下走,在Socket函数中,接下来调用了createImpl()方法,方法里面调用了create()方法。

在那之后,在try catch块中调用了DualStackPlainSocketImpl对象的bind和Conect方法,DualStackPlainSocketImpl对象中的的bind和Conect方法,调用的是本地的方法库,调用的是C中的bind和connect方法,C语言中的bind方法和connect方法在Java API中就不在深究了。

12-13 15:35