最好得列出在服务器中的文件,只有这样才能下载,用户不能凭空捏造文件。
客户端:文件上传:
如果文件与服务器的文件重名那怎么办?
解决方案:
1,不容许下载,只能修改文件名;
2,或则让该文件去代替源文件。
网络编程的一般架构:
服务器端的创建流程:
1.创建服务器的端口号:
该过程主要是创建端口号的套接字;
2.创建该端口号的线程:
A.我们引入线程池的概念,有多少socket的链接,我们就创建多少线程; 理由:如果来一个线程,我们就创建一个线程,那么导致服务器的响应速度会非常的慢。这就好比酒店的门童,如果门口来一个客户,我们就招聘一个服务员,那么将导致服务过程会相当的慢。换一种方式:何不事先就就招聘一堆服务员呢?这就是线程池的引入模型。当然如果线程初始化时创建太多也会有麻烦,他会占用太多的内存空间,对性能影响不能小觑。所以必须有量的制约。)
B.线程的主要功能。
首先在线程中我们必须找到套接字和通信协议;很显然这是该线程的私有变量。在run函数中主要就是进行service服务功能。
在这里我们就不得不介绍service服务接口。
通信协议必须完成service接口的功能。通信协议的参数就是套接字。
3.通信协议时已知的,将通信协议传入到该线程中。
所谓的通信协议就是服务器所能完成的一般功能。
这里就指真正的套接字服务程序。
客户端创建的一般架构:
首先我们必须了解:对于用户来说,他只是关心他所要实现的功能,他没有必要去了解通行协议。在这个实例中我们创建了一个接口,在该接口里
有上传和下载功能。这也就是说,用户知道这个接口就足够了,其他的的什么都不用操心。
那么由谁来完成这个接口呢?当然这就是我们程序猿的事。
在这个实例里,完成接口的是RemoteCalc类。很显然参数就是客户端的套接字。在这个类里完成上传和下载功能的具体实现过程。
具体客户端的创建流程:
1. 首先你给出你要连接的对象和端口号。这就是客户端的套接字。
2. 在RemoteCalc类里完成功能即可。
以下的是同样的模型用来完成简单的计算。
public interface Calc { // 非常简单的接口定义,Calc.java
public int add(int i, int j);
public int sub(int i, int j);
}
//最后,创建一个客户端调用主程序,为了比较,也调用了一个
//客户端的计算器的本地对象。
public class CalcClient { // CalcClient.java,客户端主程序
public static void main(String[] args) throws Exception {
Calc c1 = new CalcImpl(); // 客户端虚拟机内部的计算器对象
Calc c2 = new RemoteCalc("localhost", 4321); // 远程对象的本地代理
while(true)
{
System.out.println("call local object:");
System.out.println("5 + 6 = " + c1.add(5, 6));
System.out.println("11 - 5 = " + c1.sub(11, 5));
System.out.println("call remote object:");
System.out.println("5 + 6 = " + c2.add(5, 6));
System.out.println("11 - 5 = " + c2.sub(11, 5));
}
}
}
public class CalcImpl implements Calc { // CalcImpl.java,Calc接口的实现类
public int add(int i, int j) {
return i + j;
} // 注意:本类并没有成员数据
public int sub(int i, int j) {
return i - j;
} // 没有共享冲突这个现象
public static void main(String[] args) { // 完成对add,sub方法的测试
Calc c = new CalcImpl();
System.out.println("5 + 6 = " + c.add(5, 6));
System.out.println("11 - 5 = " + c.sub(11, 5));
}
}
import java.io.*;
import java.net.*;
public class CalcProtocol implements IOStrategy { // CalcProtocol.java
Calc c = new CalcImpl(); // 此语句如果转移到service方法中会如何?
public void service(Socket socket) { // 本例子中只有一个协议对象,那么
try { // 计算器对象也只有一个,多线程共享的。
// 如将服务器端的Calc对象的创建定义在方法内,会产生什么样的变化?
// Calc c = new CalcImpl();
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(
socket.getOutputStream());
int command = 0;
while (true) {
command = dis.readInt(); // 实际上,协议命令的数值
switch (command) { // 没有多大的意义
case 1: // 命令1映射到add方法
dos.writeInt(c.add(dis.readInt(), dis.readInt()));
dos.flush();
break;
case 2: // 命令2映射到sub方法
dos.writeInt(c.sub(dis.readInt(), dis.readInt()));
dos.flush();
break;
}
}
} catch (Exception e) {
System.out.println("client disconnected.");
}
}
}
public class CalcServer { // CalcServer.java,服务器主程序
public static void main(String[] args) {
new NwServer(4321, new ThreadSupport(new CalcProtocol()));
}
}
public interface IOStrategy { // IOStrategy.java
public void service(java.net.Socket socket); //对传入的socket对象进行处理
}
//下面是修改后的线程类,该程序对同步控制,wait和notify方法的调用要求较高
import java.net.*;
public class IOThread2 extends Thread {
private Socket socket = null;
private IOStrategy ios = null;
public IOThread2(IOStrategy ios) { // 请比较上一节中的IOThread类的构造方法
this.ios = ios; // 有何不同?
}
public boolean isIdle() { // 如果socket变量为空,那么这个线程当然是空闲的
return socket == null;
}
public synchronized void setSocket(Socket socket) {
this.socket = socket; // 传递给这个阻塞的线程一个“任务”,并唤醒它。
notify();
}
public synchronized void run() { // 这个同步方法并不是保护什么共享数据,
while (true) { // 仅仅因为wait方法调用必须拥有对象锁
try {
wait(); // 进入线程体后,立刻进入阻塞,等待状态
ios.service(socket); // 被唤醒后,立刻开始执行服务协议
socket = null; // 服务结束后,立刻返回到空闲状态
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
import java.net.*;
public class NwServer { // NwServer.java,负责接受连接请求,并将创建的Socket对象
// 通过IOStrategy接口传递给ThreadSupport对象
public NwServer(int port, IOStrategy ios) { // 这个方法将在主线程中执行
try {
ServerSocket ss = new ServerSocket(port);
System.out.println("server is ready");
while (true) {
Socket socket = ss.accept(); // 负责接受连接请求,
ios.service(socket); // 将服务器端的socket对象传递给
} // ThreadSupport对象
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
}
import java.io.*;
import java.net.*;
public class RemoteCalc implements Calc {
private DataInputStream dis = null;
private DataOutputStream dos = null;
public RemoteCalc(String host, int port) throws Exception {
Socket s = new Socket(host, port); // 这个Socket对象创建完毕后何时销毁?
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
}
public int add(int i, int j) {
try {
dos.writeInt(1); // 将加法操作映射成命令1,然后发送命令
dos.writeInt(i); // 发送加法操作第一个操作数
dos.writeInt(j); // 发送加法操作第二个操作数
dos.flush();
return dis.readInt();
} catch (Exception e) { // 将检查异常转换为运行异常并抛出
throw new ArithmeticException(e.getMessage());
}
}
public int sub(int i, int j) {
try {
dos.writeInt(2); // 将加法操作映射成命令2,然后发送命令
dos.writeInt(i); // 发送减法操作第一个操作数
dos.writeInt(j); // 发送减法操作第二个操作数
dos.flush();
return dis.readInt();
} catch (Exception e) {
throw new ArithmeticException(e.getMessage());
}
}
}
import java.util.*; //下面的程序可以替换ThreadSupport.java
import java.net.Socket;
public class ThreadPoolSupport implements IOStrategy { // ThreadPoolSupport.java
private ArrayList threads = new ArrayList();
private final int INIT_THREADS = 50;
private final int MAX_THREADS = 100;
private IOStrategy ios = null;
public ThreadPoolSupport(IOStrategy ios) { // 创建线程池
this.ios = ios;
for (int i = 0; i < INIT_THREADS; i++) {
IOThread2 t = new IOThread2(ios); // 传递协议对象,但是还没有socket
t.start(); // 启动线程,进入线程体后都是wait
threads.add(t);
}
try {
Thread.sleep(300);
} catch (Exception e) {
} // 等待线程池的线程都“运行”
}
public void service(Socket socket) { // 遍历线程池,找到一个空闲的线程,
IOThread2 t = null; // 把客户端交给“它”处理
boolean found = false;
for (int i = 0; i < threads.size(); i++) {
t = (IOThread2) threads.get(i);
if (t.isIdle()) {
found = true;
break;
}
}
if (!found) // 线程池中的线程都忙,没有办法了,只有创建
{ // 一个线程了,同时添加到线程池中。
t = new IOThread2(ios);
t.start();
try {
Thread.sleep(300);
} catch (Exception e) {
}
threads.add(t);
}
t.setSocket(socket); // 将服务器端的socket对象传递给这个空闲的线程
} // 让其开始执行协议,向客户端提供服务
}
本文深入探讨网络编程的基本架构,包括服务器端与客户端的创建流程、线程管理、通信协议和服务接口设计等内容。通过具体示例讲解如何实现文件上传下载及简单计算功能。
10万+

被折叠的 条评论
为什么被折叠?



