Phục vụ nhiều chương trình CLIENT

Cùng một lúc, một chương trình Server có thể trao đổi với nhiều chương trình Client. Server chạy thường trực trên máy chủ và các chương trình Client trên mạng có thể kết nối với Server bất kỳ lúc nào [2], Để Server có thể kết nối đồng thời với nhiều Client thl ta có thể sử dựng các luồng thực hiện như đã trình bày ờ chương I. Server thiết lâp việc kết nối với nhiều Client như sau.

while(true){

Socket connectToClient = serverSocket.accept();

Thread thread = new ThreadClass(connectToClient);

thread.start();

)

Socket server có thể có nhiều kết nối. Mồi vòng lập của chu trình while tạo ra một kết nối mới. Khi đã thiết lập được một kết nối, một luồng thực hiện (thread) mới được tạo ra để đảm nhận việc trao đòi giũa Server và Client. Sau đó Server chờ những yêu cầu kết nối khác. Nghĩa là có nhiều kết nối thực hiện theo cơ chế đa luồng thục hiện đồng thời.

Trong khi trao đồi với nhau, nhiều khí ta muốn biết những ai đang kết nối với Server. Đẻ biết về chưcmg trình khách đang kết nối với Server, tasừ dụng lớp InetAddress để xác định tên miền (host name) và địa chỉ Ip.

InetAddress ClientInetAddress = connectToClient.getlnetAddress();

System.out.println(“Host name cua Client: ” + clientNo + ” la ” + clientlnetAddress.getHostName());

System.out.prin.tln(“Dia chi IP cua Client: ” + clientNo + ” la ” + clientlnetAddress.getHostAddress());

1. Xây dựng chương trinh ứng dụng độc lập

Ví dụ 3.2. Xây dựng chương trình MultìThreadServer cho phép nhận được dữ liệu đồng thời từ nhiều Client. Mỗi kết nối thực hiện trên một luồng. Server liên tục nhận các số đo bán kính của hình tròn, tính diện tính và gửi kết quả tương ứng cho từng Client.

// MultiThreadServer.java: Server tạo ra n luồng để xử lý đồng thời yêu cầu của //n Client.

import j ava.io.*; import j ava.net.*; public class MultiThreadServer{ public static void main(String arg[]){ try {

ServerSocket serverSocket = new ServerSocket (8000); int clientNo = 1; while(true){

Socket connectToClient = serverSocket.accept (); System, out. println (“Khỏi động luồng cho Client: ” + clientNo) ;

// Xác định tên miền và địa chỉ IP của Client có số hiệu clientNo InetAddress dientlretíddress = cmnectTcClient. getlnetMdress () ; System cut.println (“Host name cua Client: ” + clientỈNb + ” la ” + clientInetAjdress.getHostNferre());

System out. println (‘Tia chi IP cua Client: ” + clientNb + ” la ” + clientInetMdress.getHostAddress());

IfendlePClient thread = dqat IfendlePClient (acnnectTcClimt) ; thread.start(); clientNo++;

}

}

}catch(IOException ex){

System.err.prìntln(ex);

}

}

}

// Định nghĩa lớp kế thừa từ Thread để xử lý một kểt nối mới

class HandleACllent extends Threađí private Socket connectToClient; public HandleAClìent(Socket socket){ connectToClient = socket;

}

// Cài đặt hàm run() để thực hiện theo luồng mới được tạo ra

public void run (){ try {

Datalnputstream isFromClient = new Datalnputstream ( connectToClient.getInputstream ()) ; DataOutputStream osToClient = new DataOutputStream( connectToClient.getoutputstream()); while(true){

double radius = isFromClient.readDouble(); Systan.out.println(“Ean kinh nhan tu client: ” + radius); double area = radius * radius * Math.PI;

osToClient.writeDouble(area); osToClient.flush();

System, out. println( “Hĩnh tron co ban kinh:    ”  +

radius + ” CO dial tích: ” + area);

}

}catch(IOException ex){

System.err.println(ex);

}

}

Trong chương trình trên, các luồng thực hiện được tạo ra để xù lý sự trao đồi giữa các Client với Server. Các luồng này thực hiện độc lập với nhau. Mồi luồng tạo ra một vùng đệm (buffer) đọc và ghi để nhận và gửi dữ liệu cho Client tương ứng.

Chạy hai chương trình Client đề thực hiện trên hai luồng trao đổi đồng thời với Server trong môi trường ƠCreator (http://www icreator com) ta có kết quà như hình 3.4.

2. Xây dựng ứng dụng nhúng applet

Sau đây chúng ta hãy xét cách kết nối một applet với một Server.

Ví dụ 3.5. Chương trình cho phép sinh viên đăng ký (ghi danh) theo học một khóa đào tạo trên mạng. Sinh viên gửi những thông tin của minh về cho Server. Server nhận và ghi vào tệp truy cập ngẫu nhiên.

Trước tiên ta xây dựng RegistrationServer; để nhận các thông tin mà sinh viên đăng ký từ chương trinh applet và ghi lên tệp ngầu nhiên.

// Student.java: Định nghĩa lớp Student bao gồm những thông tin cần đăng ký: họ tên,

// địa chỉ (phố, huyện, xã) và thành phố.

import java.io.*; public class Student{ private String name, street, city; final static int NAME_SIZE = 32; final static int STREET_SIZE = 32; final static int CITY_SIZE = 20; public Student(){

}

public Student(String n, String s, string c){ name = n; street = s; city = c;

}

// Ghi thông tin lên tệp theo kích thước cố định

public void writeStudent(DataOutput out) throws IOException{

FixedLengthStringlO.writeFixedLengthString(name, NAME_SIZE, out);

FixedLengthStringio.writeFixedLengthString(street, STREET_SIZE, out);

FixedLengthStringIO.writeFixedlengthString(city, CITY_SIZE, out);

}

// Đọc thông tin từ tệp theo kích thước cố định

public void readstudent(Datalnput in) throws IOException{

name = FixedLengthStringlO.readFixedLengthString (NAME_SIZE, in);

street = FixedLengthStringlO.readFixedLengthString (STREET SIZE, in);

city – FixedLengthStringlO.readFixedLengthString CITY_SIZE, in) ;

}

public String getName(){return name;} public String getstreet(){return street;} public string getcity(){return city;} public void setName(String s){name = s; } public void setstreet(String s)(street = s; } public void setcity(String s){city = s; }

}

// FixedLengthStnnglO.java: Định nghía lớp đọc/ghi các xâu theo kích cỡ cố định class FixedLengthStringlO{

// Đọc một xâu có độ dài cố định

public static String readFixedLengthString(int size, Datalnput in) throws IOException{ char[] c = new char[size]; for(int i — 0; i < size; i + +) c[i] = in.readchar(); return new string(c);

}

// Oil một xâu có độ dài cố định

public static void writeFixedLengthString(String s, int size, DataOutput out) throws IOException{ chart] cBuffer = new char[size]; s.getchars(0, s.length(), cBuffer, 0); for(int i = s.lengthO; i < cBuff er. length; i + +) cBuffer[i] = •

String news = new string(cBuffer); out.writeChars(news);

}

// Registrations erver. java: chương trình nhận các thông tin đăng ký cùa sinh viên và

// ghi lèn tệp ngẫu nhiên.

import java.io.*;

import java.net.*;

import j ava.awt.*;

import iava.util.Date;

import j avax.swing.*;

public class RegístrationServer extends JFrame{ private static JTextArea j taLog;

// File dùng để ghi thông tin đăng ký cùa sinh viên

private static RandomAccessFile raf = null;

public static void main(String args[]){

RegistrationServer server = new RegistrationServer();

}

public RegistrationServer(){

// Thiết lập màn hình như hình 3.4.b

JScrollPane scroll = new JScrollPane(jtaLog = new JTextAreaO ) ;

getContentPane().add(scroll, BorderLayout.CENTER);

setsize(300, 300);

setTitle(“Dich vu dang ky”);

setvisible(true);

// Mở một tệp Ở Server try {

// Mờ tệp có tên “student.dat” để đọc /ghi

raf = new RandomAccessFíle(“student.dat”, “rw”);

}catch(IOException ex){

jtaLog.append(new Date() + Error: ” + ex); System.exit(0);

} try!

//Tạo lập một socket

ServerSocket serverSocket = new ServerSocket (8000) ; jtaLog. append (new Dated + Khời động Server mái\n”);

// Đem số luồng (số sinh viên đăng ký) int count = 1; while(true){

Socket socket = serverSocket.accept(); jtaLog.append(new Date() + Client: ” + socket.getlnetAddress()■getHostAddress() + “được kết nối\n”); // Khỏi động một luồng mới phục vụ cho việc đăng ký của Client new RegistratìonThread(socket, count++).start();

}

}catch(lOException ex){

jtaLog.append(new Date() + “: Error: ” + ex) ;

}

}

// Viết thông tin đã đãng ký vào tệp

private synchronized static void writeToFile(Student student)! try!

// Chuyển về cuối tệp

raf.seek(raf.length());

student.writeStudent(raf);

// Hiển thị thông tin đã nhận được

j taLog. append(“— Thông tin sau đã được ghi vào tệp\n ” ) ;

String s = student.getName0 + “\n” + student.getStreet0 +

“\n” + student.getcity() + “\n”; j taLog.append(s);

}catch(IOException ex) {

jtaLog.append(new Date() +          ” + ex);

}

}

// Định nghĩa luồng để nhận yêu cầu đăng ký cùa Client class RegistratíonThread extends Thread{

private Socket socket; private int clientNo;

// Tạo lập vùng đệm để nhận tin private BufferedReader in;

// Tạo lập luồng để xừ lý yêu cầu đãng ký

public RégistrationThread(Socket socket, int clientNo){ this.socket = socket; this.clientNo = clientNo;

jtaLog. append (netf DateO + “:Thread: ” + clientNo +

“được khời động\n”) ;

// Tạo lập một dòng vào để nhận tù Client try {

ìn = new BufferedReader(

new InputstreamReader(socket.getlnputstream{)));

}catch(IOExceptìon ex){

jtaLog.append(new Datei) +  ” + ex);

}

}

public void run(){

string name, Street, city; try (

// Nhận tin từ client

name = new string(in.readLine()); street = new string(in.readLine0); city = new string(in.readLine());

// Tạo lập đối tượng sinh viên

Student student = new student(name, street, city); writeToFile(student);

)catch (IOException ex) {

System.out.println(ex);

}

}

}

// RegistrationClient.java: chương trình chạy cà chế độ độc lập lẫn chế độ Applet, // nhận các thông tin đãng ký cùa sinh viên và gửi về cho mảy chủ.

import java.io.*;

import java.net.*;

ímpo rt j ava.awt.*;

import j avax.swing.border. * ;

import j ava.awt.event.*;

import javax.swing.* ;

public class RegistrationClient extends JApplet implements

ActionListener{

private static JTextArea j taLog;

// Thiết lập nút “Register” để đăng ký

private JButton jbRegister = new JButton(“Register”); private boolean isStandalone = false;

JTextField jtfName = new JTextField(32);

JTextField jtfstreet = new JTextField(32);

JTextField jtfcity = new JTextField (20);

Container container;

JPanel p;

public void init(){
makePanel() ;

container = getContentPane() ; container.add(p, BorderLayout.CENTER); container.add{jbRegister, BorderLayout.SOUTH);

// Lắng nghe sự kiện khi người sử dụng nhấn nút Register jtfName.addActionListener(this); jbRegister.addActìonListener(this);

}

// Thiết lập màn hình chức năng đế nhập thông tin như hình 3.5.a

public void makePanel(){

  p = new JPanel () ;

JPanel pi = new JPanel();

pi.setLayout(new GridLayout(3, 1)); pi.add(new JLabel(“Name”)); pi.add(new JLabel(“Street”)); pi.add(new JLabel(“City”));

JPanel p2 = new JPanel();

p2.setLayout(new GridLayout(3, 1));

p2.add(j tfName);

p2.add(j tfStreet);

p2.add(j tfcity);

p.setLayout(new BorderLayout()); p.add(pl, BorderLayout.WEST); p.add(p2, BorderLayout.EAST);

}

public void actionPerformed(ActionEvent e){ if(e.getSource () == jbRegister) ( try {

Socket socket; if(isStandalone)

socket = new Socket(“localhost”, 8000); else

socket = new Socket(getCodeBase0.getHost0, 8000); // Lập dòng output để gửi cho server PrintWriter toServer =

new PrintWriter(socket.getOutputStream(), true); // Đọc các trường thông tin từ Client

Student s = new Student(jtfName.getText().trim 0,

j tfStreet.getText () .trim(), jtfCity.getText () .trim());

// Gửi các thông tin nhận được cho server toServer.prìntln(s.getName()); toServer.printIn(s.getStreet()); toServer.printIn(s.getcity());

}catch (IOException ex){

System.out.println(ex);

}

}

}

public static void main(String args[]){

JFrame frame = new JFrame(“Hệ thống đăng ký học tập”);

// Lập một frame

RegistrationClient applet = new RegistrationClient(); applet.isStandalone = true;

// Đưa applet vào frame

frame.getContentPane().add(applet,BorderLayout.CENTER); applet.init(); applet.start();

// Hiển thị frame

frame.pack();

frame.setvisible(true);

}

}

Chưonng trinh Server nhận thông tin đăng ký từ nhiều máy Client trên mạng. Mỗi khi có một Client kết nối với Server để đăng ký thì nó tạo ra một luồng thực hiện (thread) để xừ lý yêu cầu cùa khách.

Chương trinh Client có thể chạy độc lập cũng như được nhúng vào Web browser (IE) dưới dạng applet. Muốn chương trình chạy được dưới dạng applet thì phải nhúng vào thẻ APPLET CODE như đã nêu và nó sử dụng getCodeBase () .getHost () để xác định được địa chỉ I p của máy chủ.

Sau khi dịch và thực hiện, các chương trình trên cho kết quả ví dụ như hình 3.5.

/lon Jul 25 1 5:22 30 ICT 2005 Khol dong Server mot Mon Jut 25 1 5:23:07 ICT 2005 Chant: 1 27^0 0 1 duoc ket not Mon Jul 25 1 5:23:07 ICT 2005: Tnread 1 duoc khới dong Thong tin sau da duoc ghi vao tep Doan Nhat Quang 1 8 Duong Hoang Quoc viel Ha Noi

Hình 3.5.b) Chương trình Server nhận các (lanh sách đăng ký trên mạng

Lưu ý: Mô hình Client-Server nêu tren đểu sứ dụng hướng kết nối. Đầu tiên Server được khởi động và chờ đợi một sự kết nối từ Client. Sau khi kết nối được thiết lập, hai bên luân phiên nhau gửi, nhận dữ liệu thông qua đường truyền như hình 3.6.