62、服务器端的实现

我们还是先实现服务器端的功能。你可以先参考前面的描述的界面,先把界面设计好,由于比较简单,这里我就不讲解了。

一、实现服务器对应每个客户端的线程类。

在com.dao包中新建一个Server2ClientThread类,该类是服务器端类的核心功能,其实现思路与上一天所讲的差不多,也是每一个客户端就对应一个线程来维护,不同的是,服务器需要记录所有的在线用户和信息,而这些信息是所有的用户共享的,所以这里我们把这些变量定义为静态变量(static),这点要注意。实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

/**

 * 服务器对应每个客户端的线程类

 

 * @author Administrator

 

 */

public class Server2ClientThread extends Thread {

    private Socket socket;// 对应客户端

    private String name;// 客户姓名

    private PrintWriter out;// 输出流

    private BufferedReader in;// 输入流

    // 所有的客户端,这个变量所有的线程共享,所以使用静态变量的方式。

    private static List<Socket> sockets = new ArrayList<Socket>();

    // 记录所有的人的信息

    public static List<String> messages = new ArrayList<String>();

    // 记录所有额人的名字

    public static List<String> names = new ArrayList<String>();

    public Server2ClientThread(Socket socket) {

        this.socket = socket;

        sockets.add(socket);// 记录所有的socket

        try {

            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(

                    socket.getOutputStream())), true);

            in = new BufferedReader(new InputStreamReader(

                    socket.getInputStream()));

        catch (IOException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

        }

    }

    @Override

    public void run() {

        // 接收客户的用户名

        try {

            name = in.readLine();

            names.add(name);

        catch (IOException e1) {

            // TODO Auto-generated catch block

            e1.printStackTrace();

        }

        // 负责与客户通讯

        out.println(name + ",欢迎你登陆:");

        out.flush();

        try {

            while (true) {

                String message = in.readLine();

                if (message == null) {

                    break;

                }

                messages.add(name + "说:" + message + "   ");

                // 转发给所有的socket

                for (Socket so : sockets) {

                    PrintWriter soout = new PrintWriter(new BufferedWriter(

                            new OutputStreamWriter(so.getOutputStream())), true);

                    soout.println(name + "说:" + message);

                    soout.flush();

                }

            }

            sockets.remove(socket);

            names.remove(name);

        catch (IOException e) {

            // 如果客户端发生异常就从列表删除

            sockets.remove(socket);

            names.remove(name);

            // e.printStackTrace();

        }

    }

}

二、实现服务器端的功能类。

我们把启动服务和关闭服务功能封装成为两个方法,在com.dao中新建类ServerStart。实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

/**

 * 启动服务器的监听 

 * @author Administrator

 *

 */

public class ServerStart {

     

    private ServerSocket server;

    /**

     * 启动

     * @param port

     */

    public void start(int port){

        try {

            server = new ServerSocket(port);

            System.out.println("服务器端运行了:端口是:"+port);

            int i = 0;

            while (true) {

                // 不断的等待客户的连接,每一次连接都产生一个新的socket

                Socket socket = server.accept();

                i++;

                 

                System.out.println("有客户端连接了。");

                 

                // 启动一个独立的线程负责与之通讯

                Server2ClientThread clientThread = new Server2ClientThread(

                        socket);

                 

                //启动线程

                clientThread.start();

            }

        catch (IOException e) {

            // TODO Auto-generated catch block

            //e.printStackTrace();

        }finally{

            try {

                server.close();

            catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

        }

    }

     

    public void close(){

        System.out.println("服务器关闭");

        try {

            server.close();

        catch (IOException e) {

            // TODO Auto-generated catch block

            //e.printStackTrace();

        }

    }

}

三、绑定按钮事件。

接下我们绑定前台的启动按钮的单击事件,由于程序运行ServerSocket的accept()时会产生阻塞程序的效果,为了不影响界面程序的正常运行,所以我们需要把Socket服务的启动放入另外一个线程中,异步运行。

用户点击了启动后,同时会不断的刷新在线用户和最新聊天信息,所以这里我们有定义一个线程处理这个事情,每个500毫秒就读取保存在Server2ClientThread中的静态变量(保存了在线用户和最新聊天信息),以达到刷新界面的效果。

用户点击了启动后,按钮将会变成关闭。用户在此点击按钮就表示关闭服务。

实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {

        if (jButton1.getText().equals("启动")) {

            // 获得用户输入的端口

            final int port = Integer.parseInt(jTextField1.getText());

            // 启动另外一个线程监听客户端

            new Thread() {

                public void run() {

                    serverStart = new ServerStart();

                    serverStart.start(port);

                }

            }.start();

            // 启动一个线程不断更新用户列表的信息和聊天信息

            new Thread() {

                public void run() {

                    while(true){

                         

                        try {

                            Thread.sleep(500);

                        catch (InterruptedException e) {

                            // TODO Auto-generated catch block

                            e.printStackTrace();

                        }

                         

                        StringBuffer usersb=new StringBuffer();

                        for(String name:Server2ClientThread.names){

                            usersb.append(name+"\r\n");

                        }

                         

                        StringBuffer messagesb=new StringBuffer();

                        for(String message:Server2ClientThread.messages){

                            messagesb.append(message+"\r\n");

                        }

                         

                        userjTextArea.setText(usersb.toString());

                        messagejTextArea.setText(messagesb.toString());

                    }

                }

            }.start();

            jButton1.setText("关闭");

        else {

            // 关闭服务

            serverStart.close();

            jButton1.setText("启动");

        }

    }

好,我们的服务器端功能就完成了。

猜你喜欢

转载自blog.csdn.net/liubao616311/article/details/83753457