代码

=======================

俗话说,“talking is cheap, show me your code”,下面是一个带有UI界面的JAVA网络聊天程序,使用Socket连接完成通信。

JAVA服务端程序


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * Server
 */
public class Server {
    private static final int PORT = 4000;   // 指定通信端口
    public final String END_OF_MESSAGE = "$$";

    public void launch() throws IOException {
        ServerSocket server = new ServerSocket(PORT);
        Socket requSocket = server.accept();
        
        InputStream inStream = requSocket.getInputStream();
        OutputStream outputStream = requSocket.getOutputStream();

        Scanner in = new Scanner(inStream);
        PrintWriter out = new PrintWriter(outputStream, true /* autoFlush */);

        out.println("Welcome! Let's talk.");
        while (in.hasNextLine()) {
            String line = in.nextLine();
            if (line.equals("Hello")) {
                out.println("Hi");
            } else if (line.equals("Bye")) {
                out.println("Bye");
                out.println(END_OF_MESSAGE);
                break;
            } else {
                out.println("Can't understand what you said!");
            }
        }
        in.close();
        out.close();
        server.close();
    }

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

客户端程序


import java.io.*;
import java.awt.event.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;

public class Client {

    private static final String IP = "127.0.0.1";   // 使用本机地址
    private static final int PORT = 4000;
    private final String LINK_FAIL = "====================\n Connection build failed!\n";
    private final String UNKOWN_HOST = "====================\n Unkown host!\n";
    private final String IO_EXCEPTION = "====================\n I/O wrong!\n";
    public static final String END_OF_MESSAGE = "$$";

    private Socket userSocket;
    private String userName;
    private InputStream inStream;
    private Scanner in;
    private OutputStream outStream;
    private PrintWriter out;
    protected boolean serverStop;

    public Client(String name) {
        userName = name;
    }

    public void launch() {
        UserInterface.creatUI(userName);    // 创建一个用户界面
        boolean error = false;
        try {
            creatConnection();
        } catch (ConnectException e) {
            UserInterface.chatWindow.append(LINK_FAIL);
            error = true;
        } catch (UnknownHostException e) {
            UserInterface.chatWindow.append(UNKOWN_HOST);
            error = true;
        } catch (IOException e) {
            UserInterface.chatWindow.append(IO_EXCEPTION);
            error = true;
        }
        if (error) {
            shutDown();
            return;
        }
        String greeting;
        while ((greeting = in.nextLine()) == null)
            ;
        serverStop = false;
        UserInterface.chatWindow
                .append(new SimpleDateFormat("MM-dd HH:mm").format(new Date()) + "\n" + "Server: " + greeting + "\n\n");
        UserInterface.sendButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                String message = UserInterface.inputText.getText();
                if (serverStop || message.equals(""))
                    return;

                out.println(message);

                String currentTime = new SimpleDateFormat("MM-dd HH:mm").format(new Date());
                UserInterface.chatWindow.append(currentTime + "\n" + userName + ": " + message + "\n\n");
                UserInterface.inputText.setText("");
                UserInterface.inputText.requestFocus();

                String line = in.nextLine();
                if (line.equals(END_OF_MESSAGE)) {
                    serverStop = true;
                    shutDown();
                    UserInterface.sendButton.setEnabled(false);
                } else {
                    currentTime = new SimpleDateFormat("MM-dd HH:mm").format(new Date());
                    UserInterface.chatWindow.append(currentTime + "\n" + "Server: " + line + "\n\n");
                }
            }
        });

    }

    private void creatConnection() throws ConnectException, NullPointerException, IOException {
        try {
            userSocket = new Socket(IP, PORT);
            inStream = userSocket.getInputStream();
            in = new Scanner(inStream);
            outStream = userSocket.getOutputStream();
            out = new PrintWriter(outStream, true /* autoFlush */);
        } catch(ConnectException connectException) {
            throw connectException;
        } catch(NullPointerException nullPointerException) {
            throw nullPointerException;
        } catch(IOException ioException) {
            throw ioException;
        }
    }

    private void shutDown() {
        in.close();
        out.close();
        try {
            userSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Client client = new Client("Tinshine");
        client.launch();
    }
}

用户界面

使用JAVA的swing组件实现一个简单的聊天窗口



import javax.swing.*;
import java.awt.*;

/**
 * UserInteface
 */
public class UserInterface {

    private final static String APP_NAME = "MSN";
    public static String nameOfUser;

    private static JFrame frame;
    private static JPanel background;
    private static JPanel sendPanel;
    public static JTextArea chatWindow;
    public static JTextField inputText;
    public static JButton sendButton;
    public static JScrollPane chatWinJScrollPane;
    public static JScrollBar chatWinJScrollBar;
    public static Point chatWinJScrollPoint;

    public static void creatUI(String userName) {
        nameOfUser = userName;
        
        frame = new JFrame(APP_NAME);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        background = new JPanel();
        background.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
        background.setLayout(new BoxLayout(background, BoxLayout.Y_AXIS));

        chatWindow = new JTextArea(10, 10);
        chatWindow.setLineWrap(true);
        chatWinJScrollPane = new JScrollPane(chatWindow);
        chatWinJScrollBar = chatWinJScrollPane.getVerticalScrollBar();
        chatWinJScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        chatWinJScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        background.add(chatWinJScrollPane);

        sendPanel = new JPanel();
        sendPanel.add(new JLabel(userName + ": "));
        inputText = new JTextField(20);
        inputText.requestFocus();
        sendPanel.add(inputText);
        sendButton = new JButton("Send");
        sendPanel.add(sendButton);
        background.add(sendPanel);

        frame.getContentPane().add(background);
        frame.pack();
        frame.setVisible(true);
    }
}

设计过程

  1. 服务端使用ServerSocket()类创建一个Socket通信连接,使用accept()方法等待客户端请求。
  2. 客户端使用Socket()类,根据指定的IP地址和端口号创建一个到服务端的Socket连接,随后就可以与服务端进行通信。
  3. UI界面主要负责打印客户端和服务端之间发送的消息,显示客户端用户姓名以及消息的发送时间。
    效果如下:
    Step1: 启动客户端
    JAVA Socket API与LINUX Socket API探究教程
    Step2: 发送问候语句,得到服务器答复
    JAVA Socket API与LINUX Socket API探究教程
    Step3:发送告别语句,结束Socket连接
    JAVA Socket API与LINUX Socket API探究教程

API调用栈追踪

=============================

=======================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================

以服务端为例,设置断点,追踪调用栈,得到如下所示的调用关系:

JAVA Socket API与LINUX Socket API探究教程

在实例化ServerSocket类的时候,构造函数会调用ServerSocket类的bind()方法,后者会调用继承自抽象类AbstractPlainSocketImplPlainSocketImplsocketBind()方法。在socketBind()中会调用用native关键字标注的bind0()方法。从而实现将一个socket连接绑定到指定的本地IP地址和端口号。

  • native关键字标注的方法为原生方法,一般是用其他语言写成的函数,常用来实现java语言对操作系统底层接口的访问。JAVA语言本身不能直接对操作系统底层进行操作,但是JAVA允许程序通过JAVA本机接口JNI,使用C/C++等其他语言实现这种操作。

接着,同样的步骤,从ServerSocket类的listen()一直追溯到PlainSocketImplsocketListen()方法的listen0()。该方法主要为了设置允许的最大连接请求队列长度,当请求队列满时,拒绝后来的连接请求。

最后,同样,从ServerSocket类的accept()追溯到accept0(),等待连接请求的到来。

JAVA Socket接口与LINUX Socket接口对比

===================================================

作为对比,LINUX提供的相应Socket API分别为:


// 创建一个套接字
int socket(itn domain, int type, int protocol);
// 将套接字绑定到地址上
int bind(int sockfd, const struct sockaddr *addr, socklen_t addelen);
// 监听连接
int listen(int sockfd, int backlog);
// 接收一个连接请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

JAVA通过调用这些系统API来实现它的底层功能。不同之处在于,JAVA将这一切全都封装起来,这使得面向网络的编程对于JAVA程序员来说变得十分简单,我们只需要知道使用哪一个类(实际上就是ServerSocketSocket两个类),为它们传入必要的地址参数,就能够轻松实现Socket通信。

  • 在window操作系统中,使用native标注的本地方法在编译时会生成一个动态链接库(.dll文件)为JAVA语言提供相应的本地服务。

标签: Socket, JAVA, String, API, static, private, new, public

相关文章推荐

添加新评论,含*的栏目为必填