网络编程
练习:上传图片。
客户端:
1.建立端点。
2.读取客户端已有的图片数据。
3.通过Socket输出流将数据发给服务端。
4.读取服务端反馈信息。
5.关闭。
import java.net.*;
import java.io.*;
class Demo2
{
public static void main(String []args) throws Exception{
Socket s = new Socket(InetAddress.getByName("192.168.0.102"),10001);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
byte[] buf = new byte[1024];
int len = 0;
while((len = bis.read(buf))!=-1){
bos.write(buf,0,len);
bos.flush();
}
s.shutdownOutput();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line = br.readLine())!=null){
System.out.println(line);
}
bis.close();
s.close();
}
}
服务端:
import java.net.*;
import java.io.*;
class Demo2
{
public static void main(String []args) throws Exception{
ServerSocket ss = new ServerSocket(10001);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....连接成功");
int count = 1;
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("2.jpg"));
byte[] buf = new byte[1024];
int len = 0;
while((len = bis.read(buf))!=-1){
bos.write(buf,0,len);
bos.flush();
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("上传成功");
bos.close();
s.close();
}
}
分析:这个服务端有个局限性,当A客户端连接上以后,被服务端获取到,服务端就执行具体流程。这时B客户端连接,只有等待。因为服务端还没有处理完A客户端的请求,还没有循环回来执行下一次accept方法,所以暂时获取不到B客户端对象。
解决:那么,为了可以让多个客户端同时并发访问服务端,就要将每个客户端封装到一个单独的线程中。这样就可以同时处理多个客户端请求了。

答:只要明确了每一个客户端要在服务端执行的代码即可。将该代码存入run方法中。
服务端优化:
import java.net.*;
import java.io.*;
class Demo2
{
public static void main(String []args) throws Exception{
ServerSocket ss = new ServerSocket(10001);
while(true){
Socket s = ss.accept();
new Thread(new UpLoad(s)).start();
}
}
}
class UpLoad implements Runnable
{
private Socket s;
UpLoad(Socket s){
this.s = s;
}
public void run(){
try{
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....连接成功");
//避免覆盖之前的文件
int count = 1;
File file = new File(ip+".jpg");
while(file.exists()){
file = new File(ip+"("+(count++)+")"+".jpg");
}
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
byte[] buf = new byte[1024];
int len = 0;
while((len = bis.read(buf))!=-1){
bos.write(buf,0,len);
bos.flush();
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("上传成功");
bos.close();
s.close();
}
catch(Exception e){
throw new RuntimeException("上传失败");
}
}
}
客户端优化:
import java.net.*;
import java.io.*;
class Demo1
{
public static void main(String []args) throws Exception{
//对指定文件进行判断
if(args.length==0){
System.out.println("请输入有效文件。");
return;
}
if(args.length!=1){
System.out.println("请输入一个有效文件。");
return;
}
if(!args[0].endsWith(".jpg")){
System.out.println("仅支持.jpg文件。");
return;
}
File file = new File(args[0]);
if(!file.exists()&&file.isFile()){
System.out.println("文件不存在。");
return;
}
if(file.length()>1024*1024*3){
System.out.println("文件大小超过5m。");
return;
}
else
UpLoad(args,file);
}
public static void UpLoad(String[] args,File file) throws Exception{
Socket s = new Socket(InetAddress.getByName("192.168.0.102"),10001);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
byte[] buf = new byte[1024];
int len = 0;
while((len = bis.read(buf))!=-1){
bos.write(buf,0,len);
bos.flush();
}
s.shutdownOutput();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line = br.readLine())!=null){
System.out.println(line);
}
bis.close();
s.close();
}
}
练习:客户端通过键盘录入用户名,服务器对这个用户名进行校验。如果存在,在服务端显示“XXX,已登录”。并在客户端显示“XXX,欢迎光临”。如果该用户不存在,则在服务端显示“XXX,尝试登陆”。并在客户端显示“XXX,该用户不存在”。最多只能登陆三次。
客户端:
import java.net.*;
import java.io.*;
class Demo1
{
public static void main(String []args) throws Exception{
Socket s = new Socket(InetAddress.getByName("192.168.0.102"),10001);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
for(int x=0;x<3;x++){
String line = br.readLine();
if(line==null)
break;
pw.println(line);
BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
String str = bufr.readLine();
System.out.println(str);
if(str.contains("欢迎"))
break;
}
br.close();
s.close();
}
}
服务端:
import java.net.*;
import java.io.*;
class Demo2
{
public static void main(String []args) throws Exception{
ServerSocket ss = new ServerSocket(10001);
while(true){
Socket s = ss.accept();
new Thread(new loadThread(s)).start();
}
}
}
class loadThread implements Runnable
{
private Socket s;
loadThread(Socket s){
this.s = s;
}
public void run(){
try{
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....连接成功");
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream
()));
boolean flag = false;
String name = null;
for(int x=0;x<3;x++){
name = br.readLine();
if(name == null)
break;
String uname = null;
BufferedReader bru = new BufferedReader(new FileReader("user.txt"));
while((uname = bru.readLine())!=null){
if(uname.equals(name)){
flag = true;
break;
}
else
flag = false;
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
if(flag){
System.out.println(name+",已登录");
pw.println(name+",欢迎光临");
break;
}
else{
System.out.println(name+",尝试登陆");
pw.println(name+",该用户不存在");
}
}
br.close();
s.close();
}
catch(Exception e){
throw new RuntimeException("校验失败");
}
}
}
练习:客户端和服务端
1.客户端:浏览器。
2.服务端:自定义。
服务端:
import java.net.*;
import java.io.*;
class Demo1
{
public static void main(String []args) throws Exception{
ServerSocket ss = new ServerSocket(10001);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....连接成功");
OutputStream out = s.getOutputStream();
out.write("客户端你好".getBytes());
s.close();
}
}
练习:查看浏览器给服务端发的东西
import java.net.*;
import java.io.*;
class Demo1
{
public static void main(String []args) throws Exception{
ServerSocket ss = new ServerSocket(10001);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....连接成功");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
OutputStream out = s.getOutputStream();
out.write("客户端你好".getBytes());
s.close();
}
}
分析:服务端收到浏览器发来的http请求消息头。
注:空行必须要有,因为要用于区分数据体和数据头。
练习:模拟浏览器向Tomcat服务器获取数据。
import java.net.*;
import java.io.*;
class Demo1
{
public static void main(String []args) throws Exception{
Socket s = new Socket(InetAddress.getByName("192.168.0.102"),8080);
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("GET /myweb/index.html HTTP/1.1");
pw.println("Accept:*/*");
pw.println("Accept-Language: zh-CN");
pw.println("Host: 192.168.0.102:8080");
pw.println("Connection: Keep-Close");
pw.println();
pw.println();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
}
}
注:上面部分是应答消息头。
URL:统一资源定位符。可以自动截取字符串。
例:
import java.net.*;
import java.io.*;
class Demo
{
public static void main(String []args) throws Exception{
URL url = new URL("http://localhost:8080/myweb/index.html?name=haha&age=30");
//获取主机
String host = url.getHost();
//获取端口
int port = url.getPort();
//获取路径
String path = url.getPath();
//获取文件名
String file = url.getFile();
//获取参数
String query = url.getQuery();
System.out.println("Host:"+host);
System.out.println("Port:"+port);
System.out.println("Path:"+path);
System.out.println("File:"+file);
System.out.println("Queryt:"+query);
}
}
还可以直接进行连接,不用关闭资源。
import java.net.*;
import java.io.*;
class Demo
{
public static void main(String []args) throws Exception{
URL url = new URL("http://localhost:8080/myweb/index.html");
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
}
}
URL是应用层,Socket是传输层。所以用URL连接对象不会出现多余的应答消息头。
URI:功能比URL强大,例:条形码。
练习:IE图形化界面。
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
class DemoIE
{
private Frame f;
private TextField tf;
private Button b;
private TextArea ta;
private Dialog d;
private Label l;
DemoIE(){
init();
}
public void init(){
f = new Frame("MyIE");
f.setBounds(400,150,700,500);
f.setLayout(new FlowLayout());
tf = new TextField(70);
f.add(tf);
b = new Button("转到");
f.add(b);
ta = new TextArea();
ta.setRows(26);
ta.setColumns(78);
f.add(ta);
d = new Dialog(f,"提示");
d.setBounds(500,300,300,100);
d.setLayout(new FlowLayout());
f.setVisible(true);
myEvent();
}
public void myEvent(){
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
tf.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_ENTER){
load();
}
}
});
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
load();
}
});
}
public void load(){
ta.setText("");
try{
String str = "http://"+tf.getText();
URL url = new URL(new String(str));
String host = url.getHost();
int port = url.getPort();
if(port == -1)
port = 80;
String file = url.getFile();
System.out.println("http://"+host+":"+port+"//"+file);
url = new URL("http://"+host+":"+port+"//"+file);
URLConnection conn = url.openConnection();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream
()));
String line = null;
while((line = br.readLine())!=null){
ta.append(line+"\r\n");
}
}
catch(Exception e){
System.out.println("nono");
}
}
public void showDia(String info){
l.setText(info);
d.setVisible(true);
}
public static void main(String []args){
new DemoIE();
}
}
知识补充:
java.net.SocketAddress
|—java.net.InetSocketAddress:封装了地址和端口。
|—java.net.InetSocketAddress:封装了地址和端口。
DatagramSocket(SocketAddress bindaddr)
创建数据报套接字,将其绑定到指定的本地套接字地址。
ServerSocket:ServerSocket(int port, int backlog)
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
ServerSocket(int port, int backlog, InetAddress bindAddr)
使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
backlog:队列的最大长度。指能连接到服务器的最大连接数。
浏览器的动作:
解析:当输入完地址回车以后,浏览器会先从本地计算机上解析目标IP,这个IP收录在C:\Windows\System32\drivers\etc\hosts中。如果没有,才会到DNS服务器中去寻找。然后会返回一个ip地址给浏览器,这样浏览器就能通过ip地址找到相应的主页了。