Java 网络编程---分布式文件协同编辑器设计与实现
目录:
第一部分:Java网络编程知识
(一)简单的Http请求
一般浏览网页时,使用的时Ip地址,而IP(Internet Protocol,互联网协议)目前主要是IPv4和IPv6.
IP地址是一个32位整数,一般分成4个八位二进制,为了方便记忆一般将八位整数换算为一个0-255的十进制整数。
利用Http的这些知识就可以实现一个多线程下载器,以及爬虫的基础向web站点发送GET/POST请求:
(1)一个简单的多线程下载器
import java.net.*;
import java.io.RandomAccessFile;
import java.io.InputStream;
public class DownUtil
{
//下载路径
private String path;
//指定下载文件存储的位置
private String targetFile;
//定义使用多少线程下载
private int threadNum;
//定义下载线程对象
private DownThread[] threads;
//定义下载文件的总大小
private int fileSize; public DownUtil(String path,String target,int threadNum)
{
this.path = path;
this.targetFile=target;
this.threadNum=threadNum;
//初始化threads数组
threads=new DownThread[threadNum];
}
public void download() throws Exception
{
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
conn.setRequestProperty(
"Accept",
"image/gift,image/jpeg,image/pjpeg,image/pjpeg"
+"application/x-shockwave-flash,application/xaml+xml"
+"application/vnd.ms-xpsdocument,application/x-ms-xbap"
+"application/x-ms-application,application/vnd.ms-excel"
+"application/vnd.ms-powerpoint,application/msword,*/*"
);
conn.setRequestProperty("Accept-Language","zh-CN");
conn.setRequestProperty("Charset","UTF-8");
conn.setRequestProperty("Connection","Keep-Alive");
//获得文件大小
fileSize = conn.getContentLength();
conn.disconnect();
int currentPartSize=fileSize/threadNum+1;
RandomAccessFile file = new RandomAccessFile(targetFile,"rw");
//设置本地文件大小
file.setLength(fileSize);
file.close();
for(int i=0;i<threadNum;i++)
{
//计算每个线程开始的位置
int startPos = i*currentPartSize;
//每个线程使用一个RandomAccessFile下载
RandomAccessFile currentPart=new RandomAccessFile(targetFile,"rw");
//定位线程下载的位置
currentPart.seek(startPos);
//创建下载线程
threads[i]=new DownThread(startPos,currentPartSize,currentPart);
//启动线程
threads[i].start();
}
}
//获取下载的完成比
public double getCompleteRate()
{
//统计多个线程已经下载的总大小
int sumSize=0;
for(int i=0;i<threadNum;i++)
{
sumSize+=threads[i].length;
}
return sumSize*1.0/fileSize;
}
private class DownThread extends Thread
{
//当前的下载位置
private int startPos;
//当前线程负责下载的文件的大小
private int currentPartSize;
//当前线程需要下载文件块
private RandomAccessFile currentPart;
//定义该现场当前已经下载的字节数
public int length;
public DownThread(int startPos,int currentPartSize,RandomAccessFile currentPart)
{
this.startPos=startPos;
this.currentPartSize=currentPartSize;
this.currentPart=currentPart;
}
public void run()
{
try
{
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5*1000);
conn.setRequestMethod("GET");
conn.setRequestProperty(
"Accept",
"image/gift,image/jpeg,image/pjpeg,image/pjpeg"
+"application/x-shockwave-flash,application/xaml+xml"
+"application/vnd.ms-xpsdocument,application/x-ms-xbap"
+"application/x-ms-application,application/vnd.ms-excel"
+"application/vnd.ms-powerpoint,application/msword,*/*"
);
conn.setRequestProperty("Accept-Language","zh-CN");
conn.setRequestProperty("Charset","UTF-8");
InputStream inStream=conn.getInputStream();
//跳过startPos个字节,只下载自己负责的那部分文件
inStream.skip(this.startPos);
byte[] buffer=new byte[1024];
int hasRead=0;
//读取网络数据,并写入文件
while(length<currentPartSize && (hasRead=inStream.read(buffer))!=-1)
{
currentPart.write(buffer,0,hasRead);
length+=hasRead;
}
currentPart.close();
inStream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
(2)发生GET/POST请求
import java.net.URLConnection;
import java.net.URL;
import java.util.Map;
import java.util.List;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
class GetPostTest
{
/**
*想指定URL发送GET请求
*@param url 发送请求的URL
*@param param 请求参数,格式满足key=value&key2=value2的形式
*@return URL 代表远程资源的响应
*/
public static String sendGet(String url,String param)
{
String result = "";
String urlName=url+"?"+param;
try
{
URL realUrl=new URL(urlName);
//打开和URL之间的连接
URLConnection conn=realUrl.openConnection();
//设置通用的请求属性
conn.setRequestProperty("accept","*/*");
conn.setRequestProperty("connection","Keep-Alive");
conn.setRequestProperty("user-agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2906.0 Safari/537.36");
//建立实际链接
conn.connect();
Map<String,List<String>> map =conn.getHeaderFields();
//遍历所有相应头字段
for(String key:map.keySet())
{
System.out.println(key+"---->"+map.get(key));
}
try(
//定义BufferedReader输入流来读取URL响应
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8")))
{
String line;
while((line=in.readLine())!=null)
{
result+="\n"+line;
}
}
}
catch (Exception e)
{
System.out.println("发送GET请求出现异常!"+e);
e.printStackTrace();
}
return result;
}
/**
*想指定URL发送POST请求
*@param url 发送请求的URL
*@param param 请求参数,格式满足key=value&key2=value2的形式
*@return URL 代表远程资源的响应
*/
public static String sendPost(String url,String param)
{
String result="";
try
{
URL realUrl=new URL(url);
//打开和URL之间的连接
URLConnection conn=realUrl.openConnection();
//设置通用的请求属性
conn.setRequestProperty("accept","*/*");
conn.setRequestProperty("connection","Keep-Alive");
conn.setRequestProperty("user-agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2906.0 Safari/537.36");
conn.setDoOutput(true);
conn.setDoInput(true);
try(
//获取URLConnection对象对应的输出流
PrintWriter out =new PrintWriter(conn.getOutputStream()) )
{
//发送请求参数
out.print(param);
//flush输出流的缓冲
out.flush();
}
try(
//定义BufferedReader输入流来读取URL响应
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8")))
{
String line;
while((line=in.readLine())!=null)
{
result+="\n"+line;
}
}
}
catch (Exception e)
{
System.out.println("发送POST请求出现异常!"+e);
e.printStackTrace();
}
return result;
}
public static void main(String[] args)
{
//String s =GetPostTest.sendGet("https://www.jd.com",null);
//System.out.println(s);
String s1=GetPostTest.sendPost("http://my.just.edu.cn/","user=1245532105&pwd=095493");
String s2 =GetPostTest.sendGet("http://my.just.edu.cn/index.portal",null);
System.out.println(s1);
}
}
(二)基于TCP协议的网络编程(使用Socket进行通信)
TCP协议一般是和IP协议一起使用的,TCP/IP协议属于一种可靠的网络协议,它可以在通信的两端各建立一个Socket,使得两端形成一个虚拟的网络链路。于是两端的程序就可以通过这个虚拟链路进行通信。
通过安装IP协议,可以保证计算机之间发送和接受数据,但是无法解决数据分组在传输过程中的问题。所以需要TCP协议来提供可靠并且无差错的通信服务。
通过java的封装好的Socket可以实现一个简单的聊天程序。这个程序由服务端程序和客户端程序组成,为了实现用户聊天需要在服务端除了Server用于接受客户端请求并建立对应的Socket,还得通过一个ServerThread来为每个客户端建立一个线程用于监听客户端的消息,并向客户端发送消息。而在客户端除了一个Client程序用于建立和服务器端的Socket并且将用户输入的数据发送给服务器端,还需要一个ClientThread用来建立一个线程用于监听服务器端发来的数据并显示出来。
import java.io.PrintStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.Collections;
import java.util.ArrayList;
import java.lang.Thread;
class MyServer
{
public static final int SERVER_PORT=30000;
//利用MyMap保存每个客户端名字和对应的输出流
public static MyMap<String,PrintStream> clients = new MyMap<String,PrintStream>();
public void init()
{
try
(
//建立监听的ServerSocket
ServerSocket ss = new ServerSocket(SERVER_PORT))
{
//采用死循环一直接受客户端的请求
while(true)
{
Socket s= ss.accept();
new MyServerThread(s).start();
}
}
catch (IOException ex)
{
System.out.println("服务器启动失败,请检查端口:"+SERVER_PORT+"是否已经被占用?");
}
}
public static void main(String[] args)
{
MyServer server=new MyServer();
server.init();
}
}
Server
import java.net.Socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
public class MyServerThread extends Thread
{
//定义当前线程所处理的Socket
private Socket s=null;
//该Socket对应的输入流
BufferedReader br=null;
//该Socket对应的输出流
PrintStream ps=null;
public MyServerThread(Socket s) throws IOException
{
this.s=s;
}
public void run()
{
try
{
//获取该Socket对应得输入流
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//获取该Socket对应的输出流
ps=new PrintStream(s.getOutputStream());
String line = null;
//采用循环不断地从Socket中读取客户端发来的数据
while( (line=br.readLine())!=null)
{
//如果读取到MyProtrocol.USER_ROUND开始,并以其结束则可以确定读到的是用户的登录名
if(line.startsWith(MyProtocol.USER_ROUND)&&line.endsWith(MyProtocol.USER_ROUND))
{
//得到真实消息
String userName=getRealMsg(line);
//如果用户名重复
if(MyServer.clients.map.containsKey(userName))
{
System.out.println("用户名重复");
ps.println(MyProtocol.NAME_REP);
}
else
{
System.out.println(userName+"登陆成功");
ps.println(MyProtocol.LOGIN_SUCCESS);
MyServer.clients.put(userName,ps);
}
}
//如果读到以MyProtocol.PRIVATE_ROUND开始,并以其结束,则可以确定是私聊信息,私聊信息只向制定输出流发送
else if(line.startsWith(MyProtocol.PRIVATE_ROUND)&&line.endsWith(MyProtocol.PRIVATE_ROUND))
{
//得到真实消息
String userAndMsg = getRealMsg(line);
//以SPLIT_SIGN分割,前半部分是私聊用户名,后一部分是内容
//System.out.println(userAndMsg);
//System.out.println(MyProtocol.SPLIT_SIGN);
String user = userAndMsg.split(MyProtocol.SPLIT_SIGN)[0];
String msg = userAndMsg.split(MyProtocol.SPLIT_SIGN)[1];
//获取私聊用户对应的输出流,并发送私聊信息
MyServer.clients.map.get(user).println(MyServer.clients.getKeyByValue(ps)+"悄悄对你说:"+msg);
}
//公聊,对所有Socket发
else
{
//获取真实消息
String msg=getRealMsg(line);
//遍历clients中的每个输出流
for(PrintStream clientPs:MyServer.clients.valueSet())
{
clientPs.println(MyServer.clients.getKeyByValue(ps)+"说:"+msg);
}
}
}
}
//捕获到异常,表明该Socket有问题,将该程序对应的输出流从Map中删除
catch (IOException ex)
{
MyServer.clients.removeByValue(ps);
System.out.println(MyServer.clients.map.size());
//关闭网络和IO资源
try
{
if(br!=null) br.close();
if(ps!=null) ps.close();
if(s!=null) s.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
//将读到的内容去掉前后的协议,恢复成真实数据
private String getRealMsg(String line)
{
return line.substring(MyProtocol.PROTOCOL_LEN,line.length()-MyProtocol.PROTOCOL_LEN);
}
}
ServerThread
import java.io.PrintStream;
import java.io.IOException;
import java.net.UnknownHostException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetAddress;
import javax.swing.JOptionPane;
class MyClient
{
private static final int SERVER_PORT=30000;
private Socket socket;
private PrintStream ps;
private BufferedReader brServer;
private BufferedReader keyIn;
public void init()
{
try
{
//初始化代表键盘的输入流
keyIn = new BufferedReader(new InputStreamReader(System.in));
//链接到服务器
socket = new Socket("127.0.0.1",SERVER_PORT);
//获取该Socket对应的输入流和输出流
ps = new PrintStream(socket.getOutputStream());
brServer=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String tip="";
//采用循环不断弹出对话框要求输入用户名
while(true)
{
String userName=JOptionPane.showInputDialog(tip+"输入用户名");
//用户输入用户名后前后加上协议字符串后发送
ps.println(MyProtocol.USER_ROUND+userName+MyProtocol.USER_ROUND);
//读取服务器的响应
String result = brServer.readLine();
//如果用户名重复则开始下次循环
if(result.equals(MyProtocol.NAME_REP))
{
tip="用户名重复,请重试!";
continue;
}
//如果服务器返回登陆成功,则循环结束
if(result.equals(MyProtocol.LOGIN_SUCCESS))
{
break;
}
}
}
//补货到异常,关闭网络资源,并推出程序
catch (UnknownHostException ex)
{
System.out.println("找不到远程服务器,请确定服务器已经启动!");
closeRs();
System.exit(1);
}
catch(IOException ex)
{
System.out.println("网络异常!请重新登陆");
closeRs();
System.exit(1);
}
//以该socket对应的输入流启动Client线程
new MyClientThread(brServer).start();
}
//定义一个读取键盘输出,并向网络发送的方法
private void readAndSend()
{
try
{
//不断地读取键盘的输入
String line=null;
while((line=keyIn.readLine())!=null)
{
//如果发送的信息中有冒号,并且以//开头,则认为发送私聊信息
if(line.indexOf(":")>0&&line.startsWith("//"))
{
line=line.substring(2);
//System.out.println(MyProtocol.PRIVATE_ROUND+line.split(":")[0]+MyProtocol.SPLIT_SIGN+line.split(":")[1]+MyProtocol.PRIVATE_ROUND);
ps.println(MyProtocol.PRIVATE_ROUND+line.split(":")[0]+MyProtocol.SPLIT_SIGN+line.split(":")[1]+MyProtocol.PRIVATE_ROUND);
}
else
{
ps.println(MyProtocol.MSG_ROUND+line+MyProtocol.MSG_ROUND);
}
}
}
//捕获到异常,关闭网络资源,并退出程序
catch (IOException ex)
{
System.out.println("网络异常!请重新登陆");
closeRs();
System.exit(1);
}
}
//关闭Socket、输入流、输出流的方法
private void closeRs()
{
try
{
if(keyIn!=null) ps.close();
if(brServer!=null) brServer.close();
if(ps!=null) ps.close();
if(socket!=null) keyIn.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
public static void main(String[] args)
{
MyClient client=new MyClient();
client.init();
client.readAndSend();
}
}
Client
import java.io.PrintStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetAddress;
import java.lang.Thread;
class MyClientThread extends Thread
{
//该客户端线程负责处理输入流
BufferedReader br=null;
public MyClientThread(BufferedReader br)
{
this.br=br;
}
public void run()
{
try
{
String line=null;
//不断的读取Socket输入流的内容,并将其打印输出
while((line=br.readLine())!=null)
{
System.out.println(line);
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try
{
if(br!=null) br.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}
ClientThread
JDK1.4开始,Java提供了NIO API用于开发高性能的网络服务器,借助NIO可以不必为每个客户端都建立一个线程。下面我将利用NIO改变这个聊天程序。
第二部分:分布式文件协同编辑器
第一部分 Java网络编程知识
附件:《实验指导报告书》
分布式文件协同编辑器
实验指导书--1
一、实验目的
加深对分布式系统基本概念的理解,灵活运用多种分布式互斥与同步访问的算法;掌握网络编程的基本方法,熟悉Socket套接字的使用,实现网络间的通信程序;设计并初步实现一个“分布式文件协同编辑器”原型系统。
二、实验要求
1、有N个网络用户编辑同一磁盘上的多个文件,文件的存取服务由文件服务器完成,网络上的用户通过客户端软件完成协同编辑工作。编辑器架构如图1所示:
2、设计并实现分布式互斥与同步访问,实现多用户协同编辑。
3、设计并初步实现一个“分布式文件协同编辑器”。
4、可以在Window或Linux下完成。
三、实验内容
实验内容由两部分组成:
第一部分:编写文件服务器程序,详细要求有:
- 实现对磁盘文件的存取服务。
- 实现与客户端软件的文件传输服务(客户端指定文件名称,通过Socket实现)
- 不能使用集中控制策略。
第二部分:编写客户端软件,具体要求如下:
- 实现对文件简单的编辑/浏览功能(只要能查看/改变文件内容即可);
- 实现与文件服务器的文件传输功能(客户端指定文件名称,通过Socket实现);
- 实现多个客户端软件之间的通讯功能,能实现协同工作的功能;
- 实现分布式的互斥编辑功能。
四、实验方法
1、实验有两个方案(同学们也可自己设计新的方案):
方案一:获取并发用户列表的方法:客户端软件在访问文件时,先在子网内广播信息,访问该文件的其它客户端软件应答。
方案二:分布式互斥访问可以使用令牌环算法。
2、实现分布式互斥编辑功能(同学们也可自己设计新的方案):
1) 多个客户可以同时浏览文件内容(已经提交的版本)。
2) 当文件加互斥锁时,多个客户也可以同时浏览文件内容(旧版本),但是只能由加互斥锁的用户编辑文件(未提交的版本),而且提交之后,必须通知其他浏览该文件的用户,以便其它用户获得最新版本的内容。
3) 进入编辑状态之前,首先要获得互斥锁。而且在任意时刻,只能一个用户对文件进行编辑。
Java 网络编程---分布式文件协同编辑器设计与实现的更多相关文章
- 20145212 实验五《Java网络编程》
20145212 实验五<Java网络编程> 一.实验内容 1.运行下载的TCP代码,结对进行,一人服务器,一人客户端: 2.利用加解密代码包,编译运行代码,一人加密,一人解密: 3.集成 ...
- 20145213《Java程序设计》实验五Java网络编程及安全
20145213<Java程序设计>实验五Java网络编程及安全 实验内容 1.掌握Socket程序的编写. 2.掌握密码技术的使用. 3.设计安全传输系统. 实验预期 1.客户端与服务器 ...
- 20145206《Java程序设计》实验五Java网络编程及安全
20145206<Java程序设计>实验五 Java网络编程及安全 实验内容 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.设计安全传输系统. 实验步骤 我和201451 ...
- 20145337实验五Java网络编程及安全
20145337实验五Java网络编程及安全 实验内容 掌握Socket程序的编写 掌握密码技术的使用 设计安全传输系统 实验步骤 基于Java Socket实现安全传输 基于TCP实现客户端和服务器 ...
- Java 网络编程----基本概念
网络现在是一个非常普遍的概念. 以下是维基百科上的解释: 网络一词有多种意义,可解作: 网络流也简称为网络(network).一般用于管道系统.交通系统.通讯系统建模. 有时特指计算机网络. 或特指其 ...
- 20145208 实验五 Java网络编程
20145208 实验五 Java网络编程 实验内容 1.用书上的TCP代码,实现服务器与客户端. 2.客户端与服务器连接 3.客户端中输入明文,利用DES算法加密,DES的秘钥用RSA公钥密码中服务 ...
- 20145215实验五 Java网络编程及安全
20145215实验五 Java网络编程及安全 实验内容 掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验步骤 本次实验我的结对编程对象是20145208蔡野,我负责编写客 ...
- Java实验报告五:Java网络编程及安全
Java实验报告五:Java网络编程及安全 ...
- 20145211 《Java程序设计》实验报告五————Java网络编程及安全实验报告
实验内容 1.掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验步骤 这一部分是与我的partner合作的,详见他的博客- [20145326 <Java程序设计> ...
随机推荐
- Histogram Equalization
转载请注明出处. Histogram Equalization 也就是直方图均衡化, 是一种常用的通过直方图处理来增强图像的方法. 对于一副灰度图像,其像素范围一般在0~255之间,我们记nk(0&l ...
- OC高级编程——深入block,如何捕获变量,如何存储在堆上
OC高级编程——深入block,如何捕获变量,如何存储在堆上 首先先看几道block相关的题目 这是一篇比较长的 博文 ,前部分是block的测试题目,中间是block的语法.特性,block讲 ...
- 面试题 41 和为s的两个数字VS 和为S的连续整数序列
(1)和为S的两个数字 bool findNumberWithSum(int data[], int length, int sum, int &numb1, int &numb2){ ...
- 华为u8800怎样root?
用SuperOneClick就可以root了 .在手机上面进入设置-应用程序-开发-三个都要勾选.用数据线连接到电脑,确认可正常连接.不行就使用豌豆夹连接,豌豆夹会自动帮你安装手机的驱动.运行Supe ...
- Delphi 调试 通过BreakPoint
1.打个断点, 如下图 2. 在断点上,邮件,如下图 3. 弹出一个窗体 ,如下图 在 condition 中写条件就可以了. 这样就可以按你假设的条件来进行了,方便.
- map循环遍历删除
typedef map<string,int> MapFileList; int main() { MapFileList m_SingleList; m_SingleList.inser ...
- js高级程序设计(第三版)学习笔记(第一版)
ecma:欧洲计算机制造商协会iso/iec:国际标准化和国际电工委员会 dom级别(10*)文档对象模型1:DOM核心(映射基于xml文档)与dom html(在dom核心基础上)2:对鼠标,事件, ...
- HTML5新增的一些属性和功能之八——web Worker
Web Workers 为什么用web workers? 浏览器的原理中决定了页面打开只有一个主线程--UI渲染线程,如果线程中有耗时的程序(js)会阻塞线程,使得页面中其他的UI无法渲染,我们一般把 ...
- CentOS 6.3中安装OpenCV2.3.1
下面为自己测试可用的OpenCV在Linux下的安装步骤 .检查并安装相关程序,确保gtk安装成功,否则无法显示图片 yum install gcc-c++ yuminstall gtk-devel. ...
- RPG JS跨平台测试
RPG JS虽然说是跨平台的,但是在具体的测试中效果并不理想. 以官方提供的Demo为例 问题一 手机的屏幕太小,导致画面上的人物都很小,连点击都很不准确.在9寸的平板上才可以看得比较清楚. 问题二 ...