1. 程式人生 > >62、伺服器端的實現

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("啟動");

}

}

好,我們的伺服器端功能就完成了。