用JavaSocket开发高并发小型服务器
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
用JavaSocket开发高并发小型服务器一、表示Socket服务端的类:Server.java
Java代码
1public class Server extends Thread{
2public static int port = 6789;
3public static String host = "10.60.1.127";
4private static ServerSocket server = null;
5
6public void run() {
7if(server == null){
8try{
9//1、新建ServerSocket实例
10server = new ServerSocket(port);
11}catch(IOException e){
12 e.printStackTrace();
13}
14}
15
16System.out.println("服务器启动...");
17while(true){
18try{
19//2、访问ServerSocket实例的accept方法取得一个客户端Socket对象
20Socket client = server.accept();
21if(client == null) continue;
22
23new SocketConnection(client, "D:\\").start();
24
25}catch(IOException ex){
26ex.printStackTrace();
27}
28}
29}
30
31public static void main(String[] args) {
32new Server().start();
33}
34
35}
Socket客户端处理类:SocketConnection.java
Java代码
36public class SocketConnection extends Thread{
37private Socket client;
38private String filepath;
39
40public SocketConnection(Socket client, String filepath){
41this.client = client;
42this.filepath = filepath;
43}
44
45public void run(){
46if(client == null) return;
47
48DataInputStream in = null;
49DataOutputStream writer = null;
50
51try{
52//3、访问Socket对象的getInputStream方法取得客户端发送过来的数据流
53in = new DataInputStream(new
BufferedInputStream(client.getInputStream()));
54
55String fileName = in.readUTF(); //取得附带的文件名
56
57if(filepath.endsWith("/") == false && filepath.endsWith("\\") == false){
58filepath += "\\";
59}
60filepath += fileName;
61
62//4、将数据流写到文件中
63writer = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(new File(filepath)))));
64
65int bufferSize = 2048;
66byte[] buf = new byte[bufferSize];
67
68int read = 0;
69while((read=in.read(buf)) != -1){
70writer.write(buf, 0, read);
71}
72
73writer.flush();
74System.out.println("数据接收完毕");
75
76}catch(IOException ex){
77ex.printStackTrace();
78}finally{
79try{
80in.close();
81writer.close();
82client.close();
83}catch(IOException e){
84 e.printStackTrace();
85}
86}
87
88}
89
90}
二、表示客户端的类:Client.java
Java代码
91public class Client {
92private Socket client;
93private boolean connected;
94
95public boolean isConnected() { 96return connected;
97}
98
99public void setConnected(boolean connected) { 100this.connected = connected;
101}
102
103public Client(String host, int port){
104try {
105//1、新建Socket对象
106client = new Socket(host, port);
107System.out.println("服务器连接成功!"); 108
109this.connected = true;
110
111} catch (UnknownHostException e) {
112this.connected = false;
113close();
114} catch (IOException e) {
115System.out.println("服务器连接失败!"); 116this.connected = false;
117close();
118}
119}
120
121/**
122* 将文件内容发送出去
123*
124* @param filepath 文件的全路径名
125*/
126public void sendFile(String filepath){
127DataOutputStream out = null;
128DataInputStream reader = null;
129try{
130if(client == null) return;
131
132File file = new File(filepath);
133reader = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
134
135//2、将文件内容写到Socket的输出流中
136out = new DataOutputStream(client.getOutputStream());
137
138out.writeUTF(file.getName()); //附带文件名
139
140int bufferSize = 2048; //2K
141byte[] buf = new byte[bufferSize]; 142
143int read = 0;
144while((read=reader.read(buf)) != -1){ 145out.write(buf, 0, read);
146}
147
148out.flush();
149
150}catch(IOException ex){
151ex.printStackTrace();
152close();
153}finally{
154try {
155reader.close();
156out.close();
157} catch (IOException e) {
158 e.printStackTrace();
159}
160}
161}
162
163/**
164* 关闭Socket
165*/
166public void close(){
167if(client != null){
168try {
169client.close();
170} catch (IOException e) {
171 e.printStackTrace();
172}
173}
174}
175
176public static void main(String[] args) {
177Client client = new Client(Server.host, Server.port); 178if(client.isConnected()){
179client.sendFile("E:\\EKI.txt");
180client.close();
181}
182}
183
184}
2010-01-17
Java5增加了新的类库并发集java.util.concurrent,该类库为并发程序提供了丰富的API,多线程编程在Java 5中更加容易,灵活。
下列是一个范例的源码。
1、Server类源码
Java代码
1package com.jeyo.java5;
2
3import java.io.IOException;
4import .InetSocketAddress;
5import .ServerSocket;
6import .Socket;
7import java.util.concurrent.ExecutorService;
8import java.util.concurrent.Executors;
9
10public class Server {
11public static final int PORT = 19788;
12
13private ExecutorService pool = null;
14private ServerSocket serverSocket = null;
15
16public void start(){
17//创建一个可重用固定线程数的线程池
18pool = Executors.newFixedThreadPool(10);
19
20//创建一个可根据需要创建新线程的线程池
21//pool = Executors.newCachedThreadPool();
22
23try{
24serverSocket = new ServerSocket();
25serverSocket.setReuseAddress(true);
26serverSocket.bind(new InetSocketAddress(PORT));
27
28System.out.println("开始监听...");
29while(true){
30Socket socket = serverSocket.accept(); //获取客户端线程,没有则等待
31
32//异步执行
33//要是线程池繁忙,是等待还是终止?
34pool.execute(new ServiceThread(socket));
35}
36}catch(Exception e){
37 e.printStackTrace();
38}
39
40cleanup();
41}
42
43public void cleanup() {
44if(serverSocket != null){
45try{
46serverSocket.close(); //关闭服务器线程47}catch(IOException e){
48 e.printStackTrace();
49}
50}
51
52pool.shutdown(); //关闭线程池
53}
54
55public static void main(String args[]) {
56Server server = new Server();
57server.start();
58}
59
60}
2、ServiceThread类源码
Java代码
61package com.jeyo.java5;
62
63import java.io.BufferedInputStream;
64import java.io.DataInputStream;
65import java.io.DataOutputStream;
66import java.io.Serializable;
67import .Socket;
68import java.util.concurrent.ExecutorService;
69import java.util.concurrent.Executors;
70import java.util.concurrent.Future;
71import java.util.concurrent.locks.ReentrantLock;
72
73public class ServiceThread implements Runnable, Serializable {
74private static final long serialVersionUID = 0;
75private Socket connectedSocket = null;
76private static int count = 0;
77private static ReentrantLock lock = new ReentrantLock(); //重入锁:是一种递归无堵塞的同步机制
78
79ServiceThread(Socket socket) {
80connectedSocket = socket;
81}
82
83public void run(){
84increaseCount();
85int curCount = getCount();
86
87String helloString = "最新序列号:" + curCount + "\n";
88
89//该对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行
90ExecutorService executor = Executors.newSingleThreadExecutor(); 91
92Future<String> future = executor.submit(new OtherT ask()); //异步执行其它逻辑代码
93
94DataOutputStream out = null;
95try{
96//往客户端线程输出信息
97out = new
DataOutputStream(connectedSocket.getOutputStream());
98out.write(helloString.getBytes());
99
100//获取客户端传送过来的信息
101DataInputStream reader = new DataInputStream(new
BufferedInputStream(connectedSocket.getInputStream()));
102System.out.println(reader.readUTF());
103
104try{
105out.write("其它任务的结果:\n".getBytes()); 106String result = future.get(); //同步获取任务结果107out.write(result.getBytes());
108
109}catch(Exception e){
110 e.printStackTrace();
111}
112
113}catch(Exception e){
114 e.printStackTrace();
115}finally{
116if(out != null){
117try{
118out.close(); //关闭输出流
119}catch(Exception e){
120 e.printStackTrace();
121}
122}
123
124executor.shutdown(); //关闭线程执行器125}
126}
127
128private int getCount() {
129int ret = 0;
130try{
131lock.lock();
132ret = count;
133}finally{
134lock.unlock(); //切记要在finally中释放锁135}
136return ret;
137}
138
139private void increaseCount() {
140try{
141lock.lock();
142++count;
143}finally{
144lock.unlock();
145}
146}
147
148}
3、OtherTask类源码
Java代码
149package com.jeyo.java5;
150
151import java.util.concurrent.Callable;
152
153public class OtherT ask implements Callable<String> { 154public String call() throws Exception {
155return "执行了其它逻辑代码。
";
156}
157}
4、Client类源码
Java代码
158package com.jeyo.java5;
159
160import java.io.BufferedInputStream;
161import java.io.DataInputStream;
162import java.io.DataOutputStream;
163import java.io.IOException;
164import .Socket;
165
166public class Client {
167private Socket client;
168
169public Client(String host, int port){
170try{
171client = new Socket(host, port);
172}catch(IOException e){
173System.out.println("服务器连接失败!");
174close();
175}
176}
177
178public void send(){
179DataOutputStream out = null;
180try{
181//往服务器发送信息
182out = new DataOutputStream(client.getOutputStream()); 183out.writeUTF("中国");
184out.flush();
185
186//获取服务器传送过来的信息
187DataInputStream reader = new DataInputStream(new BufferedInputStream(client.getInputStream()));
188
189int bufferSize = 2048; //2K
190byte[] buf = new byte[bufferSize];
191
192int read = 0;
193while((read=reader.read(buf)) != -1){
194System.out.println(new String(buf, 0, read)); 195}
196
197}catch(IOException ex){
198ex.printStackTrace();
199}finally{
200try{
201out.close();
202}catch(IOException e){
203 e.printStackTrace();
204}
205
206close();
207}
208}
209
210private void close(){
211if(client != null){
212try{
213client.close();
214}catch(IOException e){
215 e.printStackTrace();
216}
217}
218}
219public static void main(String[] args) {
220Client client = new Client("localhost", Server.PORT);
221client.send();
222}
223
224}
远程方法调用(Remote Method Invocation,RMI)是用Java在JDK1.1中实现的,它大大增强了Java开发分布式应用的能力。
RMI目前使用Java远程消息交换协议JRMP(Java Remote Messaging Protocol)进行通信,JRMP是专为Java的远程对象制定的协议。
由于JRMP是专为Java对象制定的,因此,RMI关于用非Java语言开发的应用系统的支持不足,不能与用非Java语言书写的对象进行通信。
RMI/JNI与RMI/JDBC相结合,可帮助您利用RMI与目前使用非Java语言的现有服务器进行通信,而且在您需要时可扩展Java在这些服务器上的使用。
一、RMI运行原理
RMI应用程序通常包含两个独立的程序:服务器程序与客户机程序。
典型的服务器应用程序将创建多个远程对象,使这些远程对象能够被引用,然后等待客户机调用这些远程对象的方法。
而典型的客户机程序则从服务器中得到一个或者多个远程对象的引用,然后调用远程对象的方法。
RMI为服务器与客户机进行通信与信息传递提供了一种机制。
在与远程对象的通信过程中,RMI使用标准机制:stub与skeleton。
远程对象的stub担当远程对象的客户本地代表或者代理人角色。
调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。
在远程虚拟机中,每个远程对象都能够有相应的skeleton(在JDK1.2环境中无需使用skeleton)。
Skeleton负责将调用分配给实际的远程对象实现。
stub与skeleton由rmic编译器生成。
在RMI分布式应用程序运行时,服务器调用注册服务程序以使名字与远程对象有关联。
客户机在服务器上的注册服务程序中用远程对象的名字查找该远程对象,然后调用它的方法。
二、范例源码
1、远程传输对象
Java代码
1/**
2* 远程传输的对象务必实现Serializable接口
3*/
4public class Person implements Serializable{
5private String username;
6private String password;
7
8public Person(String username, String password){
ername = username;
10this.password = password;
11}
12
13public String getUsername() {
14return username;
15}
16public void setUsername(String username) {
ername = username;
18}
19public String getPassword() {
20return password;
21}
22public void setPassword(String password) {
23this.password = password;
24}
25}
2、远程服务的接口定义
Java代码
26/**
27* 远程服务的接口定义
28*
29* 该接口务必继承Remote,每个方法都务必抛出RemoteException特殊对象30*/
31public interface HelloWorld extends Remote {
33public Person calculate(Person p) throws RemoteException;
34}
3、远程服务接口的具体实现
Java代码
35/**
36* 远程服务接口的具体实现
37*
38* 假如一个类继承自UnicastRemoteObject,那么它务必提供一个构造函数同时声明抛出一个RemoteException对象。
39* 当这个构造函数调用了super(),它就激活UnicastRemoteObject中的代码完成RMI的连接与远程对象的初始化。
40*/
41public class HelloWorldImpl extends UnicastRemoteObject implements HelloWorld {
42private static final long serialVersionUID = -8328854041003669643L;
43
44//务必有一个显式的构造函数,同时要抛出RemoteException特殊对象
45public HelloWorldImpl() throws RemoteException{
46super();
47}
48
50System.out.println("Hello " + name);
51}
52
53public Person calculate(Person p) throws RemoteException {
54System.out.println(p.getUsername() + " _ " + p.getPassword());
55p.setPassword("new pwd");
56return p;
57}
58}
4、服务器程序
Java代码
59/**
60* 服务器程序
61*/
62public class HelloWorldServer {
63public HelloWorldServer(){
64try{
65HelloWorld obj = new HelloWorldImpl();
66//Naming.rebind("rmi://localhost:1099/HelloWorldServer", obj);
67
68//通过程序创建RMI注册服务,从而不需要执行rmiregistry命令69Registry reg = LocateRegistry.createRegistry(1099);
70reg.rebind("HelloWorldServer", obj);
71
72}catch(Exception e){
73 e.printStackTrace();
74}
75}
76
77public static void main(String[] args) {
78new HelloWorldServer();
79}
80}
5、客户端程序
Java代码
81/**
82* 客户端程序
83*/
84public class HelloWorldClient {
85public static void main(String[] args) {
86try{
87HelloWorld obj =
(HelloWorld)Naming.lookup("rmi://localhost:1099/HelloWorldServer");
88obj.say("chenjumin");
89
90Person p = new Person("cjm", "123");
91Person p2 = obj.calculate(p);
92System.out.println(p2.getUsername() + " _ " +
p2.getPassword());
93
94}catch(MalformedURLException e){
95 e.printStackTrace();
96}catch(RemoteException e){
97 e.printStackTrace();
98}catch(NotBoundException e){
99 e.printStackTrace();
100}
101}
102}
*6、用Ant命令生成桩(Stub)与框架(Skeleton)文件(自JDK1.5之后不再需要手工创建这两个文件,而由JDK通过代理机制动态生成)
Xml代码
103<project name="rmiTask" basedir="." default="rmic">
104<target name="rmic" >
105<rmic classname="rmi.server.HelloWorldImpl"
base="${basedir}/bin"/>
106</target>
107</project>
*7、启动RMI注册服务(注意:DOS命令务必在最顶层包路径所在的目录下执行)DOS命令为:rmiregistry 1099
8、启动服务端程序
DOS命令为:java rmi.server.HelloWorldServer
9、启动客户端程序
DOS命令为:java rmi.server.HelloWorldClient。