Quá trình truyền dữ liệu trên mạng diễn ra khá phức tạp. chi tiết quá trình này diễn ra tương tự như trong thực tế ta gửi thư hay bưu phẩm, trước hết ta phải ghi rỏ địa chỉ nơi đến(trường hợp này là địa chỉ IP của máy chủ), sau đó có thể gởi thông thường hay bảo đảm(tùy theo cách gởi mà thư hay bưu phẩm có chắc chắn đến được tay người nhận hay không), người nhận sau khi nhận được có thể hồi âm trả lời đã nhận đủ hay mất mát gì trong quá trình chuyển tải. người gửi có thể gửi tiếp những phần bị mất(hoặc không cần gửi nữa).
Cách chuyển dữ liệu đảm bảo dựa vào giao thức TCP(Transmission Control Protocol), còn cách chuyển không bảo đảm là dựa vào giao thức UDP(User Datagram Protocol).
8 trang |
Chia sẻ: tuandn | Lượt xem: 3664 | Lượt tải: 5
Bạn đang xem nội dung tài liệu Đồ án Chương trình đơn giản mô tả ứng dụng Client/Server và mô phỏng chức năng của các ứng dụng FTP: Truyền và nhận File, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
Phần 1: Giới thiệu
Môn : Thực hành nâng cao mạng máy tính
Giáo viên: Lê Văn Tuấn
Để tiếp cận với công nghệ thông tin đang phát triển nhanh chóng, việc truyền và nhận dữ liệu trở nên rất cần thiết. Trong khuôn khổ chương trình đã học, cũng như yêu cầu của đồ án kết thúc môn, chúng tôi đã viết một chương trình đơn giản mô tả một ứng dụng client/server, mô phỏng chức năng của các ứng dụng FTP: truyền và nhận file.
Nhóm chúng tôi gồm 5 thành viên:
Đỗ Minh Đức nhóm trưởng.
Đào Văn Thạnh nhóm viên
Lê Thị Kim Dung nhóm viên
Nguyên Tất Tuyên nhóm viên
Thái Trung Sơn nhóm viên
Sau khi đã tìm hiểu về mạng máy tính và Java chúng tôi đã đủ điều kiện để hoàn tất đồ án kết thúc môn học này. Chương trình tương đối đáp ứng các yêu cầu của đồ án đó là truyền file: upload và download.
Do thời gian hạn chế nên chương trình chỉ cài đặt các chức năng đơn giản nhất mà các ứng dụng FTP làm mà không cài đặt các chức năng mạnh nữa như resume hay truyền folder.
Phân công làm việc của nhóm:
Đỗ Minh Đức: phân tích bài toán và phân công làm việc của các thành viên, tổng hợp mã.
Đào Văn Thạnh: trình bày giao diện, viết mã truyền file đơn giản.
Lê Thị Kim Dung, Nguyễn Tất Truyên, Thái Trung Sơn: viết mã truyền file đơn giản
Phần 2: Cơ sở lý thuyết
Quá trình truyền dữ liệu trên mạng diễn ra khá phức tạp. chi tiết quá trình này diễn ra tương tự như trong thực tế ta gửi thư hay bưu phẩm, trước hết ta phải ghi rỏ địa chỉ nơi đến(trường hợp này là địa chỉ IP của máy chủ), sau đó có thể gởi thông thường hay bảo đảm(tùy theo cách gởi mà thư hay bưu phẩm có chắc chắn đến được tay người nhận hay không), người nhận sau khi nhận được có thể hồi âm trả lời đã nhận đủ hay mất mát gì trong quá trình chuyển tải. người gửi có thể gửi tiếp những phần bị mất(hoặc không cần gửi nữa).
Cách chuyển dữ liệu đảm bảo dựa vào giao thức TCP(Transmission Control Protocol), còn cách chuyển không bảo đảm là dựa vào giao thức UDP(User Datagram Protocol).
Giao tiếp theo mô hình khách chủ (Client/Server)
Khi kết nối vào máy chủ, ta có thể yêu cầu máy chủ nhiều dịch vụ khác nhau, như dịch vụ dò tìm hệ thống tên vùng DNS, chuyển tập tin…. mỗi dịch vụ này có cách gửi nhận dữ liệu theo quy ước riêng. TCP và UDP chỉ chịu trách nhiệm đưa dữ liệu từ một máy tính này đến máy tính khác, còn dữ liệu đó sẽ được gửi cho dịch vụ nào thì phải thông qua một quy định nữa là cổng(Port). mỗi dịch vụ sẽ sử dụng một cổng khác nhau để truy xuất thông tin.
Máy chủ Server sẽ quy định cổng được sử dụngcho mỗi loại dịch vụ. Thông tin giữa máy khách(Client) và máy chủ(server) phải sử dụng cổng tương ứng nhau thì mới trao đổi với nhau được.
Chương trình ở máy khách kết nối với máy chủ ở xa sau đó gửi các yêu cầu đến máy chủ, các chương trình dịch vụ trên máy chủ sẽ xử lý những yêu cầu này và gửi kết quả ngược trở về cho máy khách. Thông thường một dịch vụ trên máy chủ phục vụ cho rất nhiều máy khách.
Khái niệm socket
Trước khi yêu cầu một dịch vụ trên máy chủ thực hiện điểu gì đó, máy khách phải có khả năng kết nối được với máy chủ. Quá trình kết nối này được Java thực hiện thông qua một cơ chế trừu tượng gọi là Socket.
Nếu kết nối Socket thành công thì máy khách và máy chủ có thể trao đổi dữ liệu được với nhau thực hiện các yêu cầu về dịch vụ trên máy chủ. Việc kết nối theo cơ chế Socket cần biết hai thông tin chủ yếu đó là địa chỉ của máy cần kết nối và số hiệu cổng của chương trình dịch vụ. Java cung cấp lớp Socket ( thường dùng như “phích cắm điện” cho máy khách) và lớp ServerSocket ( thường được dùng như “ổ cắm điện” trên máy chủ). Hai lớp này được đặt trong gói thư viện java.net.
Phần 3: Nội dung chương trình
Giao diện chương trình
Chương trình gồm có 6 lớp chính:
Server, Client, Service_Client, Transport, MyFile, lib
Chức năng từng lớp :
Server : nó có nhiệm vụ tạo kết nối và lắng nghe kết nối từ các Client, từ đó sinh ra từng phân tuyến riêng phục vụ cho các Client này (Việc này là nhờ vào lớp Service_Client).
while (true)
{
try {
Socket s = ss.accept();
// khi có 1 Client kết nối vào máy chủ thì nó sẽ tạo ra đối tượng để phục vụ Client đó
new Service_Client(s);
}
catch (Exception e2)
{
e2.printStackTrace();
}
}
Service_Client extends Thread : là lớp trực tiếp đối thoại với Client kết nối vào máy chủ, do đó nó luôn lắng nghe các thông điệp từ Client gởi cho để đáp ứng. Khi mới khởi tạo nó sẽ bắt đầu lắng nghe các yêu cầu từ Client
public void run()
{
boolean stillConnect = true;
while(stillConnect) // vòng lặp vô tận nhận các thông điệp từ Client để đáp ứng
{
try
{
//nhận các thông điệp từ Client, thông điệp này được định nghĩa trong lib.class
int require = dIn.readInt();
switch (require) { //tùy vào yêu cầu nhận được mà ta có các xử lý khác nhau
case lib.M_SEND:
//cài đặt phương thức nhận khi Client cần gởi file lên
break;
………………
………………
case lib.M_REFRESH:
//cài đặt khác
break;
catch(Exception e)
{
e.printStackTrace();
stillConnect = false;
}
}
Client: tạo giao diện ứng dụng cho người dùng sử dụng các dịch vụ của Server. Khi Client đã kết nối vào server thì nó sẽ thực hiện các tác vụ do người sử dụng chọn. Với mỗi tác vụ thì Client sẽ có những thông báo cần thiết cho máy chủ (lớp Service_Client sẽ đối thoại với Client như là máy chủ ). Service_Client sẽ nhận các thông điệp này và xử lý.
private void connect(String ServerName, int port)
{
try
{
soc = new Socket(ServerName,port);
//tạo ra luồng nhập và xuất
dOut = new DataOutputStream(soc.getOutputStream());
dIn = new DataInputStream(soc.getInputStream());
oIn = new ObjectInputStream(soc.getInputStream());
btconec.setVisible(false);
btdisconec.setVisible(true);
btupload.setEnabled(true);
btdownload.setEnabled(true);
this.refreshServerFile(); //phương thức refresh lại list danh sách file của Server.
}
catch(Exception e)
{
e.printStackTrace();
btconec.setVisible(true);
btdisconec.setVisible(false);
btupload.setEnabled(false);
btdownload.setEnabled(false);
}
}
Ví dụ như trước khi Client muốn gởi một file lên Server thì Client và Service_Client sẽ có những đối thoại nhằm kiểm tra hợp lệ. nếu đối thoại thỏa thuận được thì Client sẽ gởi file cần truyền lên và Service_Client sẽ phải nhận file đó.
dOut.writeInt(lib.M_SEND); // thông báo cho Server biết là nó sẽ gởi file lên
MyFile mf = new MyFile((String)enumer.nextElement());
dOut.writeUTF(mf.getName()); //đồng thời gởi cho tên file cần truyền để Server kiểm tra
//Server sẽ nhận được yêu cầu và tên file, sau đó sẽ kiểm tra hợp lệ và báo lại cho Client
if (dIn.readInt() == lib.M_OK) //nếu bên Server đồng ý thì ta sẽ gởi file di
{
this.sendFile(mf);
}
else // nếu không đồng ý có nghĩa là trùng file thì ta sẽ có hai lựa chọn là ghi đè hay không
{
if ( JOptionPane.showOptionDialog ( this, "Exist! Overwrite? y/n", "Warning", JOptionPane.OK_CANCEL_OPTION, JOptionPane.OK_CANCEL_OPTION, null, null,null) == JOptionPane.OK_OPTION)
{
dOut.writeInt(lib.M_OK);
this.sendFile(mf);
}
else{
dOut.writeInt(lib.M_CANCEL);
}
Transport: lớp có nhiệm vụ gởi và nhận các File. Nó có hai phương thức là send và receiveFile.
Chúng ta sẽ chia nhỏ một file ra để gởi đi, mỗi gói có kích thươc là lib.P_SIZE = 4096 (byte)
Đối với gởi file đi thì nó sẽ yêu cầu có file (MyFile) và luống xuất (DataOutputStream)
public void send(MyFile mf, DataOutputStream out)
{
try{
byte b[] = new byte[lib.P_SIZE];
if (mf.isDirectory()) {//nếu mf (đối tượng file )là thư mục thì ta sẽ làm các thao tác gởi thư mục
//ở thao tác này chúng tôi không cài đặt
}
else { // ngược lại thì ta truyền file đó
long fileSize = mf.length();
out.writeLong(fileSize);//trước khi gởi thì ta gởi độ dài của file trước cho bên nhận
long packageCount = fileSize / lib.P_SIZE; //tính số gói của file
DataInputStream fi = new DataInputStream (new BufferedInputStream ( new FileInputStream( mf.getPath()))); // tạo luồng nhập từ file để đọc file đó và gởi đi.
for (long i = 0; i < packageCount; i++) {
fi.read(b,0,lib.P_SIZE); //đọc dữ liệu vào một mảng byte kích thước là lib.P_SIZE
out.write(b,0,lib.P_SIZE); // và xuất mảng đó ra luỗng xuất sang máy khác
}
if (fileSize % lib.P_SIZE != 0) //phần còn dư
{
fi.read(b,0,(int)fileSize % lib.P_SIZE);
out.write(b,0,(int)fileSize % lib.P_SIZE);
}
fi.close();
}
}
catch (Exception ee) { ee.printStackTrace(); }
}
public void receiveFile(MyFile mf, DataInputStream dIn)
{
try {
byte b[] = new byte[lib.P_SIZE]; //mảng byte để đọc dữ liệu
DataOutputStream fos = new DataOutputStream(new BufferedOutputStream(new
FileOutputStream(mf)));
long fileSize = dIn.readLong(); // nhận kích thước của file cần nhận
long packageCount = fileSize / lib.P_SIZE;
for (long i = 0; i < packageCount; i++) {//lần lượt nhận dữ liệu từ lưồng nhập và ghi nó vào file
dIn.read(b, 0, lib.P_SIZE);
fos.write(b, 0, lib.P_SIZE);
fos.flush();
}
if (fileSize % lib.P_SIZE != 0) {
dIn.read(b, 0, (int) fileSize % lib.P_SIZE);
fos.write(b, 0, (int) fileSize % lib.P_SIZE);
fos.flush();
}
fos.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
MyFile extends File : đây là lớp tiện ích tự cài đặt thêm, kế thừa từ lớp File của java có sẵn.
lib: định nghĩa các hằng số dùng cho các lớp, liên lạc.
public class lib
{
public static final int M_SEND = 1;
public static final int M_RECEIVE = 2;
public static final int M_INTERRUP = 3;
public static final int M_OK = 4;
public static final int M_CANCEL = 5;
public static final int M_START = 6;
public static final int M_STOP = 7;
public static final int M_REFRESH = 100;
public static final int P_SIZE = 4096;//1024*4
}
Những hạn chế của chương trình:
Chương trình chạy không thật sự ổn định.
Không thích hợp nếu chạy jdk1.5 vì nó không hiển thị được JfileChooser (chỉ thích hợp với jdk 1.3 – 1.4).
Không truyền đươc folder, không cho truy xuất sâu vào trong cac folder của Server.
Hướng giải quyết:
Các lỗi trên hoàn toàn có thể khắc phục được.
Phần 4: Kết luận
Mặc dù còn nhiểu hạn chế nhưng cũng đã có sự cố gắng của tất cả các thành viên trong nhóm để hoàn tất chương trình. Chương trình tuy rằng rất thô sơ nhưng cũng đã đáp ứng yêu cầu tối thiểu của một đồ án kết thúc môn học.