您的位置:首页 > 理论基础 > 计算机网络

黑马程序员_Java基础_网络编程_客户端服务端数据传输,交互,客户端请求服务原理,自定义浏览器,URL统一资源定位符

2014-05-21 10:47 1061 查看
网络编程在实际应用当中,并不只是简单的单线程应用,因为客户端和服务端之间经常存在多个客户端同时访问一个服务端的情况,服务端必须能够同时接收多个服务端的访问,这就要用到多线程技术。本节总结主要给出网络编程的一些高级应用实例。

一,需求:定义服务端和客户端,客户端上传图片文件到服务端,服务端允许同时接收多个用户上传文件。

分析:之前的上传图片的程序,服务端只允许单个客户端连接并上传图片;但是现实中是可能有多个客户端同时要访问服务端并上传文件。所以要将服务端的接受客户端的数据封装到线程中。

import java.net.*;
import java.io.*;
class TcpLoadClient
{
public static void main(String[] args) throws Exception  {
//客户端通过命令行输入文件的完整路径,如果路径错误给出相应的提示,并退出让用户重新输入
if(args.length!=1)
{
System.out.println("请选择一个jpg格式的图片");
return ;
}
File file = new File(args[0]);
if(!(file.exists() && file.isFile()))
{
System.out.println("该文件有问题,要么不存在,要么不是文件");
return ;
}
if(!file.getName().endsWith(".jpg"))
{
System.out.println("图片格式错误,请重新选择");
return ;
}
if(file.length()>1024*1024*5)
{
System.out.println("文件过大,请重新上传");
return ;
}
//创建客户端Socket对象,连接到本地服务器的指定端口
Socket s = new Socket(InetAddress.getByName("yue-PC").getHostAddress(),10090);

FileInputStream fis = new FileInputStream(file);//获取文件

//将文件数据写入到Socket输出流
OutputStream os = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len=fis.read(buf))!= -1)
{
os.write(buf,0,len);
}
s.shutdownOutput();//告诉服务端数据写入完毕

//获取服务端的反馈信息
InputStream is = s.getInputStream();
byte[] b = new byte[1024];
int len2 = is.read(b);
System.out.println(new String(b,0,len2));
s.close();
fis.close();
}
}
class TcpLoadServer
{
public static void main(String[] args) throws Exception {
//创建服务端的ServerSocket对象,端绑定到指定端口
ServerSocket ss = new ServerSocket(10090);
//循环接受客户端的请求
while(true) {
Socket s = ss.accept();
new Thread(new MyThread(s)).start();
}
}
}
//将从客户端接收的数据的处理封装成线程,因为服务端对每个客户端的操作都是一样的
class MyThread implements Runnable
{
//定义字段接受不同用户的Socket对象
private Socket s;
public MyThread(Socket s) {
this.s = s;
}
public void run() {
//定义计数器,用以区分同名文件
int count = 1;
//获取客户端IP地址
String ip = s.getInetAddress().getHostAddress();

try
{   System.out.println(ip + "已连接。");
//指定服务端存放客户端上传的文件的路径
File dir = new File("d:\\dir");//File创建的文件必须要自己新建,因为程序不会向Write那样给你自动新建
File file = new File(dir,ip + "(" + (count) + ")" + ".jpg");

//判断是否有同名文件存在,通过计数器的值创建文件名
while (file.exists())
file = new File(dir,ip + "(" + (count++) + ")" + ".jpg");
//读取客户端传送的数据,写入指定文件
FileOutputStream fos = new FileOutputStream(file);
//System.out.println("没出错");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len=in.read(buf))!=-1)
{
fos.write(buf,0,len);
}
//上传结束,给客户端反馈信息
OutputStream os = s.getOutputStream();
os.write("上传成功。".getBytes());
fos.close();
s.close();
}
catch (Exception e)
{
throw new RuntimeException("上传失败");
}

}
}


二,需求:客户端通过键盘录入用户名,服务端对这个用户名进行校验。如果该用户存在,在服务端显示xxx,已登录,并在客户端显示xxx,欢迎光临。如果该用户不存在,在服务端显示xxx,尝试登录。并在客户端显示xxx,该用户不存在,并且最多尝试三次。

import java.io.*;
import java.net.*;
//客户端
class LoginClient
{
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1",10009);
BufferedReader bufr = new
BufferedReader(new InputStreamReader(System.in));//获取键盘信息录入
PrintWriter out = new
PrintWriter(s.getOutputStream(),true);//因为是文本数据,将Socket的输出流转换成打印流
BufferedReader in = new
BufferedReader(new InputStreamReader(s.getInputStream()));//将Socket输入流转换成带缓冲区的字符读取流

for (int i=0;i<3 ;i++ )
{
String line = bufr.readLine();
if(line == null)//防止用户按ctrl+c强制结束程序,返回-1,readLine为空
break;
out.println(line);
//获取服务端的反馈信息,给用户
String info = in.readLine();
System.out.println("提示:" + info);
if(info.contains("欢迎"))
break; //如果在前两次就登陆成功,那么就结束循环
}
bufr.close();
s.close();
}
}
//服务端
class LoginServer
{
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10009);
//循环接受用户的请求
while (true)
{
Socket s = ss.accept();
new Thread(new LoginThread(s)).start();
}
}
}
//服务端对客户端的操作
class LoginThread implements Runnable
{
private Socket s;
LoginThread(Socket s) {
this.s = s;
}
public void run() {
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + "已连接...");
try
{

for (int i=0;i<3 ;i++ )
{
BufferedReader in = new
BufferedReader(new InputStreamReader(s.getInputStream()));//将Socket输入流转换成带缓冲区的字符读取流
BufferedReader bufr = new
BufferedReader(new FileReader("User.txt"));//获取数据库用户账号信息
PrintWriter out = new PrintWriter(s.getOutputStream(),true);//将Socket输出流转换成打印流,高效
//判断数据库中是否存在该用户账号信息,存在则登录成功,否则给出用户不存在提示
String name = in.readLine();
if(name==null)
break;
String line = null;
boolean flag = false;
while ((line=bufr.readLine())!=null)
{
if (line.equals(name))
{
flag = true;
break;
}
}
if(flag)
{
System.out.println(name + "已登录...");
out.println(name + "登陆成功。欢迎光临!");
break;
}
else {
System.out.println(name + "尝试登录...");
out.println(name + "用户名不存在...请重试:");
}
}
out.println("登录次数已达上限...");
s.close();
}
catch (Exception e)
{
throw new RuntimeException(ip + "检验异常。");
}
}
}


三,浏览器与服务器通信原理

浏览器与服务器之间的互相访问原理就是客户端和服务端之间进行数据传输的过程,就拿我们所使用的浏览器与Tomcat服务器来说明,浏览器之所以可以获取服务器上的数据,就是因为浏览器在向服务器发送请求,服务器接收到浏览器的请求后,分析客户端的请求,然后根据请求返回给客户想要的数据。我们可以通过自定义一个简单的服务端,然后通过浏览器访问该服务端,通过Socket流就可以获取浏览器到底发送的请求内容是什么。

1,需求:自定义一个服务端,可供多个浏览器同时访问,获取浏览器的请求信息,并打印出请求信息的内容。

import java.net.*;
import java.io.*;
class ServerDemo
{
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10001);
while (true)
{
Socket s = ss.accept();
new Thread(new ServerThread(s)).start();
}
}
}
class ServerThread implements Runnable
{
private Socket s;
ServerThread(Socket s) {
this.s = s;
}
public void run() {
System.out.println(s.getInetAddress().getHostAddress() + "访问");
try
{
//获取Socket输入流,读取浏览器发送的请求内容
InputStream in = s.getInputStream();
byte[] b = new byte[1024];
int len=in.read(b);
System.out.println(new String(b,0,len));

//给浏览器一个反馈信息
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("<font color='red' size='7'> 哈哈,连接成功...</font>");
s.close();
}
catch (Exception e)
{
throw new RuntimeException("登录异常");
}

}
}


使用猎豹浏览器的请求信息内容:

127.0.0.1访问

GET / HTTP/1.1

Accept: */*

Accept-Language: zh-CN

User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.

0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Cent

er PC 6.0)

Accept-Encoding: gzip, deflate

Host: 127.0.0.1:10001

Connection: Keep-Alive

Cache-Control: no-cache

2,自定义浏览器

通过上面的程序知道浏览器在访问服务器时发送的请求的内容后,我们可以自己定义第一个浏览器,向Tomcat服务器发送这样的请求,来访问服务器,那么我们就可以得到服务器发送回来的数据。

(1)需求:自定义一个浏览器,访问Tomcat服务器中一个html网页,通过控制台打印出服务器返回的网页数据。

import java.io.*;
import java.net.*;
class MyIE
{
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1",8080);
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("GET /myweb/Demo.html HTTP/1.1");
out.println("Accept: */*");
out.println("Accept-Language: zh-CN");
out.println("Host: 100.64.118.187:10002");
out.println("Connection: closed");//这些配置信息不一定要全部写出去,原因是有些浏览器不支持某些功能
//Connection: closed,默认是Keep—Alive(保持连接),close的意思是告诉服务端数据发完就关闭连接
//空行一定要写
out.println();
out.println();
//获取tomcat服务端发送回来的数据
BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while ((line=bufr.readLine())!=null)
{
System.out.println(line);
}
s.close();
}
}


(2)需求:编写一个简易的浏览器,在文本框中输入网址可以访问tomcat服务器的网页;

重点在于对用户输入的网址进行解析然后传给Socket协议。

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class MyIEByGUI
{
private Frame frame;
private Button okButton;
private TextField textField;
private TextArea textArea;
MyIEByGUI() {
init();
}
public void init() {
frame = new Frame("my IExplor");
frame.setBounds(400,150,600,500);
okButton = new Button("转到");
textField = new TextField(60);
textArea = new TextArea(25,70);
frame.add(textField);
frame.add(okButton);
frame.add(textArea);
frame.setLayout(new FlowLayout());

myEvent();
frame.setVisible(true);
}
public void myEvent() {
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try
{
showWeb();
}
catch (Exception exe)
{
}

}
});
textField.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
try
{
if(e.getKeyCode()==KeyEvent.VK_ENTER)
showWeb();
}
catch (Exception exe)
{
}

}
});
}
public void showWeb() throws Exception {
textArea.setText("");
String url = textField.getText();//    http://192.168.1.254:8080/myweb/demo.html int index1 = url.indexOf("//") + 2;
int index2 = url.indexOf("/",index1);
String s = url.substring(index1,index2);//    192.168.1.254:8080
String[] str = s.split(":");
int port = Integer.parseInt(str[1]);
String hostIp = str[0];
String path = url.substring(index2);
Socket so = new Socket(hostIp,port);
PrintWriter out = new PrintWriter(so.getOutputStream(),true);
out.println("GET " + path +" HTTP/1.1");
out.println("Accept: */*");
out.println("Accept-Language: zh-CN");
out.println("Host: 100.64.118.187:10002");
out.println("Connection: closed");//这些配置信息不一定要全部写出去,原因是有些浏览器不支持某些功能
//Connection: closed,默认是Keep—Alive(保持连接),close的意思是告诉服务端数据发完就关闭连接
//空行一定要写
out.println();
out.println();
//获取tomcat服务端发送回来的数据
BufferedReader bufr = new BufferedReader(new InputStreamReader(so.getInputStream()));
String line = null;
while ((line=bufr.readLine())!=null)
{
textArea.append(line + "\r\n");
}
so.close();
}
public static void main(String[] args) throws Exception {
new MyIEByGUI();
}
}


除了自定义的网页内容,还有tomcat服务端返回的应答服务头。

http应答服务头:

HTTP/1.1 200 OK 200是OK的意思,就是我这边也是HTTP1.1

Server: Apache-Coyote/1.1

Accept-Ranges: bytes

ETag: W/"215-1395457731018"

Last-Modified: Sat, 22 Mar 2014 03:08:51 GMT

Content-Type: text/html

Content-Length: 215

Date: Sat, 22 Mar 2014 04:52:52 GMT

Connection: close

(3)需求:编写一个简易的浏览器,在文本框中输入网址可以访问tomcat服务器的网页;

重点在于对用户输入的网址进行解析然后传给Socket协议。

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class MyIEByGUI
{
private Frame frame;
private Button okButton;
private TextField textField;
private TextArea textArea;
MyIEByGUI() {
init();
}
public void init() {
frame = new Frame("my IExplor");
frame.setBounds(400,150,600,500);
okButton = new Button("转到");
textField = new TextField(60);
textArea = new TextArea(25,70);
frame.add(textField);
frame.add(okButton);
frame.add(textArea);
frame.setLayout(new FlowLayout());

myEvent();
frame.setVisible(true);
}
public void myEvent() {
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//转到按钮被点击后,调用showWeb(),发送请求并获取返回内容
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try
{
showWeb();
}
catch (Exception exe)
{
}

}
});
//键盘按Enter键后也调用showWeb()方法
textField.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
try
{
if(e.getKeyCode()==KeyEvent.VK_ENTER)
showWeb();
}
catch (Exception exe)
{
}

}
});
}
public void showWeb() throws Exception {
textArea.setText("");
String url = textField.getText();//    http://192.168.1.254:8080/myweb/demo.html //获取用户输入的网址请求,切割获取目的IP和端口
int index1 = url.indexOf("//") + 2;
int index2 = url.indexOf("/",index1);
String s = url.substring(index1,index2);//    192.168.1.254:8080
String[] str = s.split(":");
int port = Integer.parseInt(str[1]);
String hostIp = str[0];
String path = url.substring(index2);
Socket so = new Socket(hostIp,port);
//将请求发送到服务器
PrintWriter out = new PrintWriter(so.getOutputStream(),true);
out.println("GET " + path +" HTTP/1.1");
out.println("Accept: */*");
out.println("Accept-Language: zh-CN");
out.println("Host: 100.64.118.187:10002");
out.println("Connection: closed");//这些配置信息不一定要全部写出去,原因是有些浏览器不支持某些功能
//Connection: closed,默认是Keep—Alive(保持连接),close的意思是告诉服务端数据发完就关闭连接
//空行一定要写
out.println();
out.println();
//获取tomcat服务端发送回来的数据
BufferedReader bufr = new BufferedReader(new InputStreamReader(so.getInputStream()));
String line = null;
while ((line=bufr.readLine())!=null)
{
textArea.append(line + "\r\n");
}
so.close();
}
public static void main(String[] args) throws Exception {
new MyIEByGUI();
}
}


四,统一资源定位符URL

它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用。

常用方法:

String getFile() 获取此 URL 的文件名。

String getHost() 获取此 URL 的主机名(如果适用)。

String getPath() 获取此 URL 的路径部分。

int getPort() 获取此 URL 的端口号。

String getProtocol() 获取此 URL 的协议名称。

String getQuery() 获取此 URL 的查询部分。

基本用法:

import java.net.*;
class URLDemo
{
public static void main(String[] args) throws Exception {
/*
URL将ip和端口,以及对服务器的请求信息封装成一个对象
*/
//URL url = new URL("http://100.64.118.187:8080/myweb/Demo.html?hahah/hh");
URL url = new URL("http://www.sina.com.cn");
URLConnection con = url.openConnection();//返回URL引用的远程对象连接
System.out.println(con);
System.out.println(url.getFile());//获取html路径以及后面的附加信息;/myweb/Demo.html?hahah/hh
System.out.println(url.getHost());
System.out.println(url.getPath());//只获取html文件的路径  /myweb/Demo.html
System.out.println(url.getPort());  //当用户没有指定端口号的时候,会默认是80端口,这个只针对http协议
System.out.println(url.getProtocol());  //获取协议名   http
System.out.println(url.getQuery());  //获取附加信息    hahah/hh
}
}


五,URL的一个重要方法:openConnection()

URLConnection openConnection()

返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。

只要URL对象调用了该方法,那么我们就不需要建立socket对象,客户端就会去连接这台主机;而URLConnection对象中有含有获取socket中的输入流和输出流,以便对服务端的数据和发送请求做基础;其实,该对象实际就是讲传输层的数据包进行了解包,送到应用层,用户那里就没有了响应头;

基本使用方法示例:

import java.io.*;
import java.net.*;
public class URLConnectionDemo {
public static void main(String[] args) throws Exception {
URL url = new URL("http://100.64.118.187:8080/myweb/Demo.html");
URLConnection uc = url.openConnection();//在内部带着协议建立连接,并且已经请求myweb/Demo.html页面,
//不需要再发送对服务端的请求了;
InputStream in = uc.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
//OutputStream out = uc.getOutputStream();
}
}


六,自定义浏览器的改进

需求:编写一个简易的浏览器,在文本框中输入网址可以访问tomcat服务器的网页。使用统一资源定位符解析用户输入的网址。

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class MyIEByGUI2
{
private Frame frame;
private Button okButton;
private TextField textField;
private TextArea textArea;
MyIEByGUI2() {
init();
}
public void init() {
frame = new Frame("my IEexplor");
frame.setBounds(400,150,600,500);
okButton = new Button("转到");
textField = new TextField(60);
textArea = new TextArea(25,70);
frame.add(textField);
frame.add(okButton);
frame.add(textArea);
frame.setLayout(new FlowLayout());

myEvent();
frame.setVisible(true);
}
public void myEvent() {
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try
{
showWeb();
}
catch (Exception exe)
{
}

}
});
textField.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
try
{
if(e.getKeyCode()==KeyEvent.VK_ENTER)
showWeb();
}
catch (Exception exe)
{
}

}
});
}
public void showWeb() throws Exception {
textArea.setText("");
String text = textField.getText();
URL url = new URL(text);
//openConnection()方法能够将服务端返回的应答消息头去掉,获取Socket流后读取到的数据中不再包含应答消息头了
URLConnection uc = url.openConnection();
//获取tomcat服务端发送回来的数据
BufferedReader bufr = new BufferedReader(new InputStreamReader(uc.getInputStream()));
String line = null;
while ((line=bufr.readLine())!=null)
{
textArea.append(line + "\r\n");
}
}
public static void main(String[] args) throws Exception {
new MyIEByGUI2();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐