黑马程序员----10网络编程

------- android培训、java培训、期待与您交流! ----------



学习笔记10网络编程

>>网络模型

OSI

TCP/IP

应用层

应用层(JAVAweb开发)

表示层

会话层

传输层

传输层

网络层

网络层

数据链路层

主机至网络层

物理层

 

>>本地回环地址127.0.0.1

 

端口号:0~65535 分为物理端口和逻辑端口,0~1024为保留和系统端口不建议使用

 

常见的通信协议为TCP与UDP都用于数据传输。

UDP

将目的和源都创建成数据包并且不需要创建连接。

每个数据包的大小在64k内

不需要创建连接,属于不可靠协议

不需要创建连接,速度快

TCP

         建立连接,形成数据传输通道

                   在连接中进行大数据量的传输

                   通过三次握手完成连接,是可靠协议(在吗?在!我知道了!)

                   必须建立连接,效率会稍低

比如视频就用UDP,因为丢几帧没关系,关键是速度,下载TCP的,因为要求质量。

 

Socket机制,我们说网络编程,其实就是Socket编程。它是为网络服务提供的一种机制。通信的两端都有Socket。网络通信其实就是Socket间的通信。数据在两个Socket键通过IO传输。

 

>> 

java中:

java.net

类 InetAddress

java.lang.Object

 java.net.InetAddress

没有构造方法

 

eg:演示获取ip地址和主机名

import java.net.*;

class netDemo

{

         publicstatic void main(String[] args)throws Exception

         {

                   show_2();

         }

        

         //获取本地IP地址和计算机名

         publicstatic void show_1()throws Exception

         {

                   InetAddressneta = InetAddress.getLocalHost();

                  

                   Stringip = neta.getHostAddress();

                   Stringname = neta.getHostName();

                  

                   System.out.println("ip="+ip+"\n"+"name= "+name);

         }

        

         //获取网络上的IP地址和计算机名

         publicstatic void show_2()throws Exception

         {

                   InetAddressneta = InetAddress.getByName("192.168.77.221");

                   //InetAddressneta = InetAddress.getByName("www.baidu.com");

                   //没有连接成功,就会返回UnknownHostException

                   //当然有时候一个域名对应了很多ip地址,那么有方法

                   /*getAllByName

                            publicstatic InetAddress[] getAllByName(String host)

                                  throwsUnknownHostException

     在给定主机名的情况下,根据系统上配置的

     名称服务返回其 IP 地址所组成的数组。 */

                  

                   Stringip = neta.getHostAddress();

                   Stringname = neta.getHostName();

                  

                   System.out.println("ip="+ip+"\n"+"name= "+name);

         }

         /*

         这里有一个伏笔:返回的两个结果都是对方的ip地址,因为本机并没有与之对应的关系表

         如果要配置这样一个对应关系,可以打开:

         C:\Windows\System32\drivers\etc\hosts文件,在其中添加

         ip地址和相对应的主机名即可,这个知识点以后会涉及

         */

}

 

 

>>UDP的发送和接收

建立发送端,接收端

建立数据包

调用Socket的发送接收方法

关闭Socket

发送端与接收端是两个独立的运行程序

 

DatagramSocket与DatagramPacket两个类

eg:演示UDP的接收端和发送端,验证时可以使用两个控制台

import java.net.*;

/*

udp发送端的思路:

1,建立udp的socket服务。

2,将要发送的数据封装成数据包。

3,通过udp的socket服务,将数据包发送取出。

4,建议关闭资源。

*/

class UDPSentDemo

{

         publicstatic void main(String[] args)throws Exception

         {

                   //1,建立udp服务。

                   DatagramSocketds = new DatagramSocket(8888);

//如果发送端并没有指定端口,那么会自动分配一个端口。

 

                   //2,定义数据内容,并将数据封装成数据包。

                   byte[]buf = "udp 哥们来了".getBytes();

 

                   DatagramPacketdp = new

DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.54.9"),10000);

 

                   //3,通过udp的socket服务中的功能,完成数据包的发送。

                   ds.send(dp);

 

                   //4,关闭资源。

                   ds.close();

         }

}

 

 

/*

udp的接收端:

思路:

1,建立udpsocket服务。需要该应用程序定义数字标识,也就是端口,

         也就是说:让该应用程序监听一个端口,只要是给这个端口发送的数据,

         该应用程序就可以进行处理。

2,通过receive方法接收数据。

3,将收到的数据存储到数据包对象中。

4,通过数据包对象的功能来完成对接收到的数据进行解析。

5,可以对资源进行关闭。

*/

class UDPReceDemo

{

         publicstatic void main(String[] args)throws Exception

         {

                   //1,定义socket服务,并监听一个端口,明确哪些是是该应用程序可以处理的。

                   DatagramSocketds = new DatagramSocket(10000);

                  

while(true) //循环保证接收端一直开着

                   {

                   //2,预先定义好一个数据包。用于存储接收到的数据。

                   //因为数据包中必须有一个容器存储这些数据。所以要先定义字节数组。

                   byte[]buf = new byte[1024];

                   DatagramPacketdp = new DatagramPacket(buf,buf.length);

 

                   //3,使用socket服务的receive方法将接收到的数据,存储到数据包中。

                   ds.receive(dp);//该方法是阻塞式。

 

                   //4,通过数据包对象的方法获取其中的数据内容。包括,地址,端口,数据主体。

                   Stringip = dp.getAddress().getHostAddress();

                   intport = dp.getPort();

 

                   Stringtext = new String(dp.getData(),0,dp.getLength());//取得发送的数据

 

                   System.out.println(ip+":"+port+"....."+text);

                   }

                   //5,关闭资源。

                   ds.close();

         }

}

 

>>结合IO流从键盘键入信息

eg:import java.net.*;

import java.io.*;

 

class UDPSentDemo

{

         publicstatic void main(String[] args)throws Exception

         {

                  

                   DatagramSocketds = new DatagramSocket();

                  

                   BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in));

                  

                   Strings = null;

                  

                   while((s= bufr.readLine())!=null)

                   {

                            byte[]buf = s.getBytes();

 

                            DatagramPacketdp = new

DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10000);

                            //这里如果用广播地址就可以群聊了

 

                            ds.send(dp);

                           

                            if(s.equals("886"))

                                     break;

                   }       

                   ds.close();

         }

}

 

>>多线程聊天室

eg:发送和接收由不同的线程控制

import java.io.*;

import java.net.*;

 

class Rece implements Runnable

{

         privateDatagramSocket ds;

         Rece(DatagramSocketds)

         {

                   this.ds= ds;

         }

         publicvoid run()

         {

                   try

                   {

                            while(true)

                            {

                                     byte[]buf = new byte[1024];

 

                                     DatagramPacketdp = new DatagramPacket(buf,buf.length);

 

                                     ds.receive(dp);

                                     Stringip = dp.getAddress().getHostAddress();

                                     intport = dp.getPort();

                 

                                     Stringtext = new String(dp.getData(),0,dp.getLength());

                                    

 

                                     System.out.println(ip+":"+port+"....."+text);

 

                                     if(text.equals("886"))

                                     {

                                               System.out.println(ip+"...离开聊天室");

                                     }

                            }

                           

                   }

                   catch(Exception e)

                   {

                            thrownew RuntimeException("接收失败");

                   }

                  

         }

}

 

class Send implements Runnable

{

         privateDatagramSocket ds;

         Send(DatagramSocketds)

         {

                   this.ds= ds;

         }

         publicvoid run()

         {

                   try

                   {

                            BufferedReaderbufr =

                                               newBufferedReader(new InputStreamReader(System.in));

 

                            Stringline = null;

 

                            while((line=bufr.readLine())!=null)

                            {

                                     byte[]buf = line.getBytes();

                                     DatagramPacketdp =

                                               newDatagramPacket(buf,buf.length,InetAddress.getByName("192.168.54.127"),10001);

 

                                     ds.send(dp);

 

                                     if("886".equals(line))

                                               break;

 

                            }

                            ds.close();

                   }

                   catch(Exception e)

                   {

                            thrownew RuntimeException("发送失败 ");

                   }

         }

}

 

class ChatDemo

{

         publicstatic void main(String[] args) throws Exception

         {

                   DatagramSocketsendSocket = new DatagramSocket();

                   DatagramSocketreceSocket = new DatagramSocket(10001);

 

                   newThread(new Send(sendSocket)).start();

                   newThread(new Rece(receSocket)).start();

 

         }

}

 

>>TCP的客户端和服务端

 

服务端只负责提供服务,不负责流的建立,因为客户端有流。

 

eg:演示TCP客户端向服务端传输一条数据,开启的时候,要先开启服务端

 

/*

TCP:面向连接,可靠的协议。

客户端和服务端。

客户端:Socket

服务端:ServerSocket

 

*/

import java.io.*;

import java.net.*;

 

/*

客户端建立思路:

1,建立客户端的socket服务。并明确要连接的服务端。

         因为只有建立了连接,才可以进行通讯。

2,如果连接建立成功,就表明,已经了数据传输的通道。就可以在该通道通过IO进行数据的读取和写入。

         该通道称为socket流,socket流中既有读取流,也有写入流。

3,通过socket对象的方法。可以获取这两个流对象。

4,通过流对象就可以对数据进行传输。

5,如果传输数据完毕,关闭资源。

*/

//通过客户端给服务端写点数据。

class ClientDemo

{

         publicstatic void main(String[] args) throws Exception

         {

                   //1,建立客户端socket服务。并去进行目的地址的连接。

                   Sockets = new Socket("192.168.54.9",10002);

 

                   //2,通过socket对象的方法getOutputStream获取输出流对象。

                   OutputStreamout = s.getOutputStream();

 

                   //3,将数据写入流中。

                   out.write("TCP哥们 又来了".getBytes());

 

                   //4,关闭资源

                   s.close();

         }

}

 

/*

建立服务端的思路:

1,建立服务端的socket服务。分配一个端口,用于监听该端口收到的数据。

2,服务端没有直接流的操作,而是通过accept方法获取客户端对象,

         在通过获取到的客户端对象的流和客户端进行通讯。

3,通过客户端的获取流对象的方法,读取数据或者写入数据。

4,如果服务完成,需要关闭客户端,然后关闭服务端。

         但是一般会关闭客户端,不关闭服务端,因为服务端是一值提供服务的。

 

*/

class ServerDemo

{

         publicstatic void main(String[] args) throws Exception

         {

                   //1, 建立服务端对象,监听一个端口。

                   ServerSocketss = new ServerSocket(10002);

 

                   //2,通过accept方法获取客户端对象。

                   Sockets = ss.accept();//该方法是阻塞式的。

                  

                   Stringip = s.getInetAddress().getHostAddress();

                   System.out.println(ip+".....connected");

 

                   //3,获取socket的读取流,读取客户端发送过来的数据。

                   InputStreamin = s.getInputStream();

 

                   //4,就是读取数据的基本操作。

                   byte[]buf  = new byte[1024];

                   intlen = in.read(buf);

 

                   Stringtext = new String(buf,0,len);

 

                   System.out.println(text);

 

                   s.close();

                   ss.close();

         }

}

 

>>在上述例子的基础上,让客户端和服务端可以通讯

eg:其实就是反向发送信息

import java.net.*;

import java.io.*;

class TCPClient

{

         publicstatic void main(String[] args)throws Exception

         {

                   SocketclientSocket = new Socket("127.0.0.1",10000);

                  

                   OutputStreamosw = clientSocket.getOutputStream();

                  

                   osw.write("TCP,Iam coming!".getBytes());

                  

                   //接收返回信息

                   InputStreamos = clientSocket.getInputStream();

                   byte[]b = new byte[1024];

                   intlen = os.read(b);

                   Stringstr = new String(b,0,len);

                   System.out.println(str);

                  

                   clientSocket.close();

         }

}

 

class TCPServer

{

         publicstatic void main(String[] args)throws Exception

         {

                   ServerSocketserverSocket = new ServerSocket(10000);

                  

                   Sockets = serverSocket.accept();

                  

                   Stringip = s.getInetAddress().getHostAddress();

                   System.out.println(ip+".....connected");

                  

                   InputStreamins = s.getInputStream();

                  

                   byte[]bytes = new byte[1024];

                   intlen = ins.read(bytes);

                  

                   Stringstr = new String(bytes,0,len);

                   System.out.println(str);

                  

                   //发送返回信息

                   OutputStreamos = s.getOutputStream();

                   //注意不要写成OutputStreamos = serverSocket.getOutputStream();

                   os.write("Igot!".getBytes());

                  

                   s.close();

                   serverSocket.close();

         }

}

 

>>练习:客户端传输文本,服务端接收并转换成大写发还给客户端

eg:有错误的代码

import java.net.*;

import java.io.*;

 

class TranClient

{

         publicstatic void main(String[] args)throws Exception

         {

                   SocketsocketClient = new Socket("127.0.0.1",10000);

                  

                   //客户端接收用户数据

                   BufferedReaderbufr =

                            newBufferedReader(new InputStreamReader(System.in));

                           

                   //客户端发送数据给服务端

                   BufferedWriterbufOutS =

                            newBufferedWriter(new OutputStreamWriter(socketClient.getOutputStream()));

                           

                   //客户端接收服务端数据

                   BufferedReaderbufInS =

                            newBufferedReader(new InputStreamReader(socketClient.getInputStream()));

                  

                   Stringstr = null;

                  

                   while((str= bufr.readLine())!=null)

                   {

                            if(str.equals("over"))

                            {

                                     break;

                            }

                           

                            bufOutS.write(str);

                           

                            Stringline = bufInS.readLine();

                            System.out.println("servers:"+line);

                   }

         }

        

}

 

class TranServer

{

         publicstatic void main(String[] args)throws Exception

         {

                   ServerSocketserverSocket = new ServerSocket(10000);//注意不要写成Socket了

                   Socketss = serverSocket.accept();

                  

                   //确认客户端的连接

                   Stringinfo = ss.getInetAddress().getHostAddress();

                   System.out.println(info+"...connected!");

                  

                   //读取客户端传输的数据

                   BufferedReaderbufInS =

                            newBufferedReader(new InputStreamReader(ss.getInputStream()));

                  

                   //将数据发送给客户端

                   BufferedWriterbufOutS =

                            newBufferedWriter(new OutputStreamWriter(ss.getOutputStream()));

                           

                   Stringstr = null;

                  

                   while((str= bufInS.readLine()) != null)

                   {

                            System.out.println(“收到数据:”+str);

                            bufOutS.write(str.toUpperCase());

                   }

         }

}

 

/*

这个代码看上去好像没有什么问题,编译通过!

分别运行了服务端和客户端

显示:127.0.0.1...connected!也没有问题。

但是在客户端输入数据,服务端没有收到,并且客户端好像被锁定了,不能输入数据

通过Ctrl+c退出,查找原因!

 

原因:BufferedReader和BufferedWriter是缓冲区,

数据写到内存去,所以,我们忘了刷新!

 

另外,bufOutS.write(str);这个语句在while((str= bufr.readLine())!=null)循环中,并且,readLine()方法的终止条件是:读取一个文本行。通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。返回的字符中不包含任何行终止符!

 

所以,即使有刷新,但客户端bufOutS.write(str);语句传出的str是不包含换行符的,服务端的while((str= bufInS.readLine()) != null)在读取的时候没有换行符,就一直认为没有终止,所以造成了客户端服务端都停止!

 

我们为此加上换行符,使用newLine()方法!

注意:服务端想客户端反馈信息也是同样的道理,要加上换行并刷新!

*/

 

eg:更正后的代码,加上刷新和换行,关闭资源

import java.net.*;

import java.io.*;

 

class TranClient

{

         publicstatic void main(String[] args)throws Exception

         {

                   SocketsocketClient = new Socket("127.0.0.1",10000);

                  

                   //客户端接收用户数据

                   BufferedReaderbufr =

                            newBufferedReader(new InputStreamReader(System.in));

                           

                   //客户端发送数据给服务端

                   BufferedWriterbufOutS =

                            newBufferedWriter(new OutputStreamWriter(socketClient.getOutputStream()));

                           

                   //客户端接收服务端数据

                   BufferedReaderbufInS =

                            newBufferedReader(new InputStreamReader(socketClient.getInputStream()));

                  

                   Stringstr = null;

                  

                   while((str= bufr.readLine())!=null)

                   {

                            if(str.equals("over"))

                            {

                                     break;

                            }

                           

                            bufOutS.write(str);

                            bufOutS.newLine();

                            bufOutS.flush();

                           

                            Stringline = bufInS.readLine();

                            System.out.println("servers:"+line);

                   }

                   bufr.close();

                   socketClient.close();

         }

        

}

 

class TranServer

{

         publicstatic void main(String[] args)throws Exception

         {

                   ServerSocketserverSocket = new ServerSocket(10000);//注意不要写成Socket了

                   Socketss = serverSocket.accept();

                  

                   //确认客户端的连接

                   Stringinfo = ss.getInetAddress().getHostAddress();

                   System.out.println(info+"...connected!");

                  

                   //读取客户端传输的数据

                   BufferedReaderbufInS =

                            newBufferedReader(new InputStreamReader(ss.getInputStream()));

                  

                   //将数据发送给客户端

                   BufferedWriterbufOutS =

                            newBufferedWriter(new OutputStreamWriter(ss.getOutputStream()));

                           

                   Stringstr = null;

                  

                   while((str= bufInS.readLine()) != null)

                   {

                            System.out.println("收到数据:"+str);

                            bufOutS.write(str.toUpperCase());

                            bufOutS.newLine();

                            bufOutS.flush();

                   }

                  

                   serverSocket.close();

                   ss.close();

         }

 

}

 

另外还可以用打印流简化代码

eg:………………

//定义目的,将数据写入到socket输出流。发给服务端。

                   //BufferedWriterbufOut =

                            //newBufferedWriter(new OutputStreamWriter(s.getOutputStream()));

                   PrintWriterout = new PrintWriter(s.getOutputStream(),true);//打印流

 

 

 

                   //定义一个socket读取流,读取服务端返回的大写信息。

                   BufferedReaderbufIn =

                            newBufferedReader(new InputStreamReader(s.getInputStream()));

 

                   Stringline = null;

                  

                   while((line=bufr.readLine())!=null)

                   {

                            if("over".equals(line))

                                     break;

                           

                            out.println(line);//自动换行和刷新!

//                         bufOut.write(line);

//                         bufOut.newLine();

//                         bufOut.flush();

 

                            Stringstr =bufIn.readLine();

                            System.out.println("server:"+str);

                           

                   }

………………

//目的。socket输出流。将大写数据写入到socket输出流,并发送给客户端。

                   //BufferedWriterbufOut =

                            //newBufferedWriter(new OutputStreamWriter(s.getOutputStream()));

 

                   PrintWriterout = new PrintWriter(s.getOutputStream(),true);//打印流

 

                   Stringline = null;

                   while((line=bufIn.readLine())!=null)

                   {

                            System.out.println(line);

 

                            out.println(line.toUpperCase());//自动换行和刷新!

//                         bufOut.write(line.toUpperCase());

//                         bufOut.newLine();

//                         bufOut.flush();

                   }

………………

 

>>能发送文本当然也能复制文件

eg:原理是在服务端建立一个文件(代码可以改进成和上传文件同名),客户端读取要复制的文件,将其中的数据发送到服务端,服务端输出到建立的文件中,然后反馈成功的消息给客户端。

 

import java.io.*;

import java.net.*;

 

class TextClient

{

         publicstatic void main(String[] args) throws Exception

         {

                   Sockets = new Socket("192.168.1.254",10006);

 

                   BufferedReaderbufr =

                            newBufferedReader(new FileReader("IPDemo.java"));

 

                   PrintWriterout = new PrintWriter(s.getOutputStream(),true);

 

                   Stringline = null;

                  while((line=bufr.readLine())!=null)

                   {

                            out.println(line);

                   }

 

                   s.shutdownOutput();//关闭客户端的输出流。相当于给流中加入一个结束标记-1.

 

                   BufferedReaderbufIn =

new BufferedReader(new InputStreamReader(s.getInputStream()));

 

                   Stringstr = bufIn.readLine();

                   System.out.println(str);

 

                   bufr.close();

 

                   s.close();

         }

}

 

class TextServer

{

         publicstatic void main(String[] args) throws Exception

         {

                   ServerSocketss = new ServerSocket(10006);

 

                   Sockets = ss.accept();

                   Stringip = s.getInetAddress().getHostAddress();

                   System.out.println(ip+"....connected");

 

 

                   BufferedReaderbufIn =

new BufferedReader(new InputStreamReader(s.getInputStream()));

 

                   PrintWriterout  = new PrintWriter(newFileWriter("server.txt"),true);

 

                   Stringline = null;

 

                   while((line=bufIn.readLine())!=null)

                   {

                            //if("over".equals(line))

                                     //break;

                            out.println(line);

                   }

 

                   PrintWriterpw = new PrintWriter(s.getOutputStream(),true);

                   pw.println("上传成功");

 

                   out.close();

                   s.close();

                   ss.close();

 

         }

}

 

特别说一下这条语句:

s.shutdownOutput();//关闭客户端的输出流。相当于给流中加入一个结束标记-1。

 

在没加这条语句时,开启服务端,开启客户端,上传。发现又一次“卡住”了!两个端都在等待。在服务端打开上传的文件,发现已经上传已经成功了,并且数据完好!

那么,这次我们使用了打印流自动换行和刷新。问题又出在哪里呢?

 

原来,对比上一个传输文本的例子,在我们客户端的“当循环”中,有一个

if(str.equals("over"))

                            {

                                     break;

                            }

语句作为结束标记,上传文件的这个例子是没有的,当然我们也可以设定这么一个结束标记,但是非常不好,因为如果上传的文本里面有over字段的话,意味着文件复制到一半就结束了!

         另外,在本例中的客户端,还有一条语句:String str = bufIn.readLine();是用来接收服务端的这条语句的:pw.println("上传成功");

         这样,原因找到了:

1、客户端的while循环没有结束标记

2、但是当读取文件结束时,while循环结束了,但是s.close();还没有被执行

3、流并没有被关闭,而是在Stringstr = bufIn.readLine();等待服务端的反馈

4、服务端并不知道客户端已经传送完毕了,因为流并没有被关闭。

5、所以服务端的while循环没有结束,而是同样等待

6、这也意味着服务端的pw.println("上传成功");没有发出,客户端不能结束等待

所以在客户端的while循环结束后,执行这样一个方法:

publicvoid shutdownOutput()

throwsIOException

 

>>并发上传图片(涉及多人的上传)

 

eg:先单人上传的情况

importjava.net.*;

importjava.io.*;

 

//客户端

classPicTranClient

{

         public static void main(String[]args)throws Exception

         {

                   Socket clientSocket = newSocket("127.0.0.1",10000);

                  

                   BufferedInputStream bufr =

                            newBufferedInputStream(new FileInputStream("PicTranDemo.bmp"));

                           

                   OutputStream out =clientSocket.getOutputStream();

                  

                   byte[] outBytes = newbyte[1024];

                   int outLen = 0;

                  

                   while((outLen =bufr.read(outBytes)) != -1)

                   {

                            out.write(outBytes,0,outLen);

                            out.flush();

                   }

                   clientSocket.shutdownOutput();

                  

                   //接收服务端反馈信息

                   InputStream in =clientSocket.getInputStream();

                  

                   byte[] b = new byte[1024];

                   int len = in.read(b);

                   System.out.println(newString(b,0,len));

                  

                   bufr.close();

                   clientSocket.close();

                  

         }

}

 

//服务端

classPicTranServer

{

         public static void main(String[]args)throws Exception

         {

                   ServerSocket serSocket = newServerSocket(10000);

                   Socket serAcc =serSocket.accept();

                   //确认客户端连接

                   String ip =serAcc.getInetAddress().getHostAddress();

                   System.out.println(ip+"...connected");

                  

                   InputStream in =serAcc.getInputStream();

                  

                   BufferedOutputStream bufw =

                            newBufferedOutputStream(new FileOutputStream("PicTranDemoRFS.bmp"));

                  

                   byte[] b = new byte[1024];

                   int len = 0;

                  

                   while((len = in.read(b)) !=-1)

                   {

                            bufw.write(b,0,len);

                            bufw.flush();

                   }

                  

                   //发送反馈信息给客户端

                   OutputStream out =serAcc.getOutputStream();

                   out.write("上传成功".getBytes());

 

                   bufw.close();

                   serAcc.close();

                   serSocket.close();

                  

         }

}

 

那么为了可以让多个客户端同时并发访问服务端。服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。

 

如何定义线程呢?

只要明确了每一个客户端要在服务端执行的代码即可。将该代码存入run方法中。

eg:演示多线程并发上传,并且防止覆盖,同时客户端限制发送条件

importjava.net.*;

importjava.io.*;

 

//客户端

classPicTranThreadClient

{

         public static void main(String[] args)throwsException

         {

                   //前面一系列判断

                   if(args.length!=1)

                   {

                            System.out.println("请选择一个jpg格式的图片");

                            return ;

                   }

 

                   //使用主线程输入方法

                   File file = newFile(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)//限制5MB的文件

                   {

                            System.out.println("文件过大,没安好心");

                            return ;

                   }

                  

                  

                   Socket clientSocket = new Socket("127.0.0.1",10000);

                  

                   BufferedInputStream bufr =

                            newBufferedInputStream(new FileInputStream(file));

                           

                   OutputStream out =clientSocket.getOutputStream();

                  

                   byte[] outBytes = newbyte[1024];

                   int outLen = 0;

                  

                   while((outLen = bufr.read(outBytes))!= -1)

                   {

                            out.write(outBytes,0,outLen);

                            out.flush();

                   }

                   clientSocket.shutdownOutput();

                  

                   //接收服务端反馈信息

                   InputStream in =clientSocket.getInputStream();

                  

                   byte[] b = new byte[1024];

                   int len = in.read(b);

                   System.out.println(newString(b,0,len));

                  

                   bufr.close();

                   clientSocket.close();

                  

         }

}

 

//服务端

classPicTranThreadServer

{

         public static void main(String[]args)throws Exception

         {

                   ServerSocket serSocket = newServerSocket(10000);

                  

                   //保证服务端一直开启

                   while(true)

                   {

                            Socket serAcc = serSocket.accept();

                           

                            new Thread(newPicThread(serAcc)).start();

                           

                   }

                  

         }

}

 

//线程类

classPicThread implements Runnable

{

         private Socket serAcc;

        

         PicThread(Socket serAcc)

         {

                   this.serAcc = serAcc;

         }

        

         public void run()

         {

                  

                   String ip =serAcc.getInetAddress().getHostAddress();

                  

                   try

                   {

                            System.out.println(ip+"...connected");

                           

                            InputStream in =serAcc.getInputStream();

                           

                            //文件名要变

                            int count = 1;

                            File file = newFile(ip+"("+count+")"+".bmp");

                            while(file.exists())//如果文件存在就改名,防止覆盖

                            {

                                     file = newFile(ip + "(" + (count++) + ")" + ".bmp");

                            }

                            BufferedOutputStreambufw =

                                     newBufferedOutputStream(new FileOutputStream(file));

                           

                           

                            byte[] b = newbyte[1024];

                            int len = 0;

                           

                            while((len =in.read(b)) != -1)

                            {

                                     bufw.write(b,0,len);

                                     bufw.flush();

                            }

                           

                            //发送反馈信息给客户端

                            OutputStream out =serAcc.getOutputStream();

                            out.write("上传成功".getBytes());

        

                            bufw.close();

                            serAcc.close();

                   }

                   catch(Exception e)

                   {

                            throw newRuntimeException(ip+"...上传失败");

                   }

         }

}

 

自己贴士:其实限制文件的功能应当封装在服务端比较安全吧!

 

>>练习:需求:

客户端通过键盘录入用户名。

服务端对这个用户名进行校验。

 

如果该用户存在,在服务端显示xxx,已登陆。

并在客户端显示 xxx,欢迎光临。

 

如果该用户存在,在服务端显示xxx,尝试登陆。

并在客户端显示 xxx,该用户不存在。

 

最多就登录三次。

 

eg:

importjava.net.*;

import java.io.*;

 

//客户端

classLoginClient

{

         public static void main(String[]args)throws Exception

         {

                   Socket clientSocket = newSocket("127.0.0.1",10000);

                  

                   BufferedReader bufr =

                            newBufferedReader(new InputStreamReader(System.in));

                           

                   PrintWriter out = newPrintWriter(clientSocket.getOutputStream(),true);

                  

                   BufferedReader burIn =

                            newBufferedReader(new InputStreamReader(clientSocket.getInputStream()));

                           

                   for(int x = 0 ; x < 3 ;x++)

                   {

                            String strOut =bufr.readLine();

                            if(strOut == null)

                            {       

                                     break;

                            }

                            out.println(strOut);

                           

                            String strIn =burIn.readLine();

                            System.out.println("serverInfo:"+strIn);

                            if(strIn.contains("欢迎"))

                            {

                                     break;

                            }

                   }

                   bufr.close();

                   clientSocket.close();

         }

}

 

//服务端

classLoginServer

{

         public static void main(String[]args)throws Exception

         {

                   ServerSocket serverSocket =new ServerSocket(10000);

                  

                   while(true)

                   {

                            Socket s =serverSocket.accept();

                           

                            new Thread(newUserThread(s)).start();

                   }

                  

         }

}

 

//多线程

class UserThreadimplements Runnable

{

         private Socket s;

         UserThread(Socket s)

         {

                   this.s = s;

         }

        

         public void run()

         {

                   String ip =s.getInetAddress().getHostAddress();

                   System.out.println(ip+"...connected");

                  

                   try

                   {

                            for(int x = 0 ; x< 3 ; x++)

                            {       

                                     //读取客户端发送的数据

                                     BufferedReaderbufIn =

                                     newBufferedReader(new InputStreamReader(s.getInputStream()));

                                     String name= bufIn.readLine();

                                     if(name==null)

                                     {

                                               break;

                                     }

                                    

                                     //载入账户文件     

                                     BufferedReaderbufr =

                                               newBufferedReader(new FileReader("UserAccount.txt"));

                                    

                                     //发送反馈信息给客户端

                                     PrintWriterout = new PrintWriter(s.getOutputStream(),true);

                                    

                                    

                                     booleanflag = false;

                                     String str= null;

                                              

                                     while((str= bufr.readLine() )!= null)

                                     {

                                               if(str.equals(name))

                                               {

                                                        flag= true;

                                                        break;

                                               }

                                     }

                                    

                                     if(flag ==true)

                                     {

                                               System.out.println(name+"...已登录!");

                                               out.println(name+"欢迎光临!");

                                     }

                                     else

                                     {

                                               System.out.println(name+"...尝试登陆!");

                                               out.println(name+"用户名不存在");

                                     }

                            }

                            s.close();

                   }

                   catch (Exception e)

                   {

                            throw newRuntimeException(ip+"校验失败");

                   }

         }

}

 

>>自定义服务端

eg:演示自定义的服务器,然后用本机的IE浏览器去访问

importjava.net.*;

importjava.io.*;

 

classLocalServerDemo

{

         public static void main(String[]args)throws Exception

         {

                  

                            ServerSocket ss =new ServerSocket(11024);

                   while(true)

                   {

                            Socket s =ss.accept();

                           

                            System.out.println(s.getInetAddress().getHostAddress()+"...connected!");

                           

                            PrintWriter pw = newPrintWriter(s.getOutputStream(),true);

                           

                            pw.println("欢迎登陆!");

                           

                            s.close();

                   }

                  

         }

}

(演示使用127.0.0.1:11024成功)

 

>>在上述例子加入语句:

 

InputStreamins = s.getInputStream();

byte[]  bytes = new byte[1024];

int len= ins.read(bytes);

System.out.println(newString(bytes,0,len));

 

获取浏览器(客户端)的请求信息如下:

GET /HTTP/1.1

Accept:image/jpeg, application/x-ms-application, image/gif, application/xaml+xml,image/pjpeg, appication/x-ms-xbap, application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword, */*

Accept-Language:zh-CN

User-Agent:Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR2.0.507

7; .NETCLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; Tablet PC2.0)

Accept-Encoding:gzip, deflate

Host:127.0.0.1:11024

Connection:Keep-Alive

 

 

 

其实信息到上一行结束。

这是国际通用的,符合HTTP协议的请求语句

1、http协议是1.1版本的

2、“Accept”表示可以接受的信息,看到jpeg和word以及powerpoint了么!

3、“Accept-Encoding”压缩方式,服务器将网页等压缩后发给客户端解压

4、目前看到的属于请求数据头,其实还有请求数据体,只是目前是空白的,头和体之间由一个空行连接。

 

>>玩一玩tomcat服务器

启动tomcat\bin\ startup.bat,启动了服务器。

在浏览器中输入127.0.0.1:8080(端口是http端口),显示了tomcat的主页

 

有了服务器和浏览器的请求信息,我们自己来写一个浏览器

eg:

importjava.net.*;

importjava.io.*;

 

classMyIE

{

         public static void main(String[]args)throws Exception

         {

                   Socket s = newSocket("127.0.0.1",8080);

                  

                   //尼玛,一定不要忘了写true啊!

                   PrintWriter out = newPrintWriter(s.getOutputStream(),true);

                  

                   //请求信息

                   out.println("GET/myWeb/test.html HTTP/1.1");

                   out.println("Accept:*/*");

                   out.println("Accept-Language:zh-CN");

                   out.println("Host:127.0.0.1:11000");

                   //out.println("Connection:Keep-Alive");

                   out.println("Connection:closed");

                  

                   out.println();

                   out.println();

                   //不要忘了http格式,要加空行

                  

                   BufferedReader bufr =

                            newBufferedReader(new InputStreamReader(s.getInputStream()));

                           

                   String line = null;

                  

                  while((line= bufr.readLine()) != null)

                   {

                            System.out.println(line);

                   }

                  

                   s.close();

         }

}

结果:

F:\java\workspace\day24>javaMyIE

HTTP/1.1200 OK

Server:Apache-Coyote/1.1

Accept-Ranges:bytes

ETag:W/"158-1331212562108"

Last-Modified:Thu, 08 Mar 2012 13:16:02 GMT

Content-Type:text/html

Content-Length:158

Date:Thu, 08 Mar 2012 13:23:11 GMT

Connection:close

 

       

               

这是我的网页

                welcome

 

               

                        abcdefg

                        abcdefg

               

       

发现结果除了html代码,还是包含响应头信息的!在传输层!

 

 

>>浏览器的图形化界面

其实Socket是一样的,只是融合了GUI技术,关键是输入域名的解析

eg:

importjava.awt.*;

importjava.awt.event.*;

import java.io.*;

importjava.net.*;

class  MyIEByGUI

{

         private Frame f;

         private TextField tf;

         private Button but;

         private TextArea ta;

        

         private Dialog d;

         private Label lab;

         private Button okBut;

 

 

         MyIEByGUI()

         {

                   init();

         }

         public void init()

         {

                   f = new Frame("mywindow");

                   f.setBounds(300,100,600,500);

                   f.setLayout(newFlowLayout());

 

                   tf = new TextField(60);

 

                   but = new Button("转到");

 

                   ta = new TextArea(25,70);

 

 

                   d = new Dialog(f,"提示信息-self",true);

                   d.setBounds(400,200,240,150);

                   d.setLayout(newFlowLayout());

                   lab = new Label();

                   okBut = new Button("确定");

 

                   d.add(lab);

                   d.add(okBut);

 

 

 

                   f.add(tf);

                   f.add(but);

                   f.add(ta);

 

 

                   myEvent();

                   f.setVisible(true);

         }

         private void  myEvent()

         {

 

                   okBut.addActionListener(new ActionListener()

                   {

                            public voidactionPerformed(ActionEvent e)

                            {

                                     d.setVisible(false);

                            }

                   });

                   d.addWindowListener(newWindowAdapter()

                   {

                            public voidwindowClosing(WindowEvent e)

                            {

                                     d.setVisible(false);

                            }

                   });

 

                   tf.addKeyListener(newKeyAdapter()

                   {

                            public voidkeyPressed(KeyEvent e)

                            {

                                     try

                                     {

                                                        if(e.getKeyCode()==KeyEvent.VK_ENTER)

                                               showDir();

                                     }

                                     catch(Exception ex)

                                     {

                                     }

                           

                            }

                   });

 

 

                   but.addActionListener(newActionListener()

                   {

                            public voidactionPerformed(ActionEvent e)

                            {

                                     try

                                     {

                                               showDir();

                                     }

                                     catch(Exception ex)

                                     {

                                     }

                                    

                                    

                            }

                   });

 

                   f.addWindowListener(newWindowAdapter()

                   {

                            public voidwindowClosing(WindowEvent e)

                            {

                                     System.exit(0);        

                            }

                   });

         }

 

         private void showDir()throws Exception

         {

 

                   ta.setText("");

                   String url =tf.getText();//http://192.168.1.254:8080/myweb/demo.html

                  

//域名解析,将域名切割

                   int index1 =url.indexOf("//")+2;

                   int index2 =url.indexOf("/",index1);

                   String str =url.substring(index1,index2);

                   String[] arr =str.split(":");

                   String host = arr[0];

                   int port =Integer.parseInt(arr[1]);

 

                   String path =url.substring(index2);

                   //ta.setText(str+"...."+path);

 

 

                   Socket s = newSocket(host,port);

                  

                   PrintWriter out = newPrintWriter(s.getOutputStream(),true);

 

                   out.println("GET"+path+" HTTP/1.1");

                   out.println("Accept:*/*");

                   out.println("Accept-Language:zh-cn");

                   out.println("Host:192.168.1.254:11000");

                   out.println("Connection:closed");

 

                   out.println();

                   out.println();

 

                   BufferedReader bufr =

new BufferedReader(newInputStreamReader(s.getInputStream()));

 

                   String line = null;

 

                   while((line=bufr.readLine())!=null)

                   {

                            ta.append(line+"\r\n");

                   }

 

                   s.close();

 

         }

 

         public static void main(String[] args)

         {

                   new MyIEByGUI();

         }

}

目前制作的“山寨版”IE浏览器没有解析引擎,并且位于传输层,IE浏览器是应用层的,并且对信息进行了拆包,只保留html代码,并且对html代码解析。

 

>>可以解析的图形化浏览器,并且改进域名解析方法

其实自己书写域名解析方法应当被封装,另外java有提供方法:

java.net

类 URL

java.lang.Object

  java.net.URL

所有已实现的接口:

Serializable

类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。另外URI是比URL更大的定义。

 

其中非常爽的方法:

StringgetFile()

          获取此 URL 的文件名。

StringgetHost()

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

StringgetPath()

          获取此 URL 的路径部分。

intgetPort()

          获取此 URL 的端口号。

StringgetProtocol()

          获取此 URL 的协议名称。

StringgetQuery()

          获取此 URL 的查询部分。web开发高点击率方法!

 

eg:演示方法

importjava.net.*;

classURLDemo

{

         public static void main(String[] args)throws MalformedURLException

         {

                   URL url = newURL("http://192.168.1.254/myweb/demo.html?name=haha&age=30");

 

                   System.out.println("getProtocol():"+url.getProtocol());

                   System.out.println("getHost():"+url.getHost());

                   System.out.println("getPort():"+url.getPort());

                   System.out.println("getPath():"+url.getPath());

                   System.out.println("getFile():"+url.getFile());

                   System.out.println("getQuery():"+url.getQuery());

 

                   /*即,如果没有指定端口,就设定默认端口80

int port = getPort();

                   if(port==-1)

                            port = 80;

 

                   getPort()==-1

                   */

         }

}

结果:

F:\java\workspace\day24>javaURLDemo

getProtocol():http

getHost():192.168.1.254

getPort():-1

getPath():/myweb/demo.html

getFile():/myweb/demo.html?name=haha&age=30

getQuery():name=haha&age=30

 

注意,没有指定端口的时候port返回-1。

 

 

另外,还有一个重要的类:

java.net

类 URLConnection

java.lang.Object

  java.net.URLConnection

直接已知子类:

HttpURLConnection,JarURLConnection

抽象类URLConnection 是所有类的超类,它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。

通常,创建一个到 URL 的连接需要几个步骤:

 

 

openConnection()                      connect()

对影响到远程资源连接的参数进行操作。 与资源交互;查询头字段和内容。

---------------------------->

时间

 

1、通过在 URL 上调用 openConnection 方法创建连接对象。

2、处理设置参数和一般请求属性。

3、使用 connect 方法建立到远程对象的实际连接。

4、远程对象变为可用。远程对象的头字段和内容变为可访问。

使用以下方法修改设置参数:

setAllowUserInteraction

setDoInput

setDoOutput

setIfModifiedSince

setUseCaches

………………

 

涉及类URL的方法:

URLConnectionopenConnection()

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

 

eg:演示URLConnection返回信息

importjava.net.*;

classURLConnectionDemo

{

         public static void main(String[]args)throws Exception//MalformedURLException,IOException

         {

                   URL url = newURL("http://192.168.1.254:8080/myweb/demo.html");

                  

                   URLConnection conn =url.openConnection();

                   System.out.println(conn);

         }

        

}

 

//结果:sun.net.www.protocol.http.HttpURLConnection:http://192.168.1.254:8080/myweb/demo.html

说明,其封装了http协议,进入应用层

 

并且,发现两个方法:

InputStreamgetInputStream()

          返回从此打开的连接读取的输入流。

OutputStreamgetOutputStream()

          返回写入到此连接的输出流。

证明其也封装了Socket

 

eg:二话不说,开启tomcat服务器,演示读取流

importjava.net.*;

importjava.io.*;

classURLConnectionDemo

{

         public static void main(String[]args)throws Exception//MalformedURLException,IOException

         {

                   URL url = newURL("http://127.0.0.1:8080/myWeb/test.html");

                  

                   URLConnection conn =url.openConnection();

                   System.out.println(conn);

                  

                   InputStream ins =conn.getInputStream();

                  

                   byte[] bytes = newbyte[1024];

                   int len = ins.read(bytes);

                  

                   System.out.println(newString(bytes,0,len));

         }

}

 

结果:

F:\java\workspace\day24>javaURLConnectionDemo

sun.net.www.protocol.http.HttpURLConnection:http://127.0.0.1:8080/myWeb/test.htm

       

               

这是我的网页

                welcome

 

               

                        abcdefg

                        abcdefg

               

       

 

惊喜的发现,响应头信息没有了!!!

 

 

>>域名解析是怎么回事呢?

我们在浏览器中输入www.baidu.com的时候,并非先去访问百度的服务器,而是寻找公网上的DNS服务器,用这个域名换取ip地址,DNS服务器将ip地址发给本机后,浏览器再用百度的IP地址访问百度的服务器。

那么在没有联网的情况下,为什么输入127.0.0.1和localhost都能访问本机服务器的页面呢?原因是,在C:\Windows\System32\drivers\etc中有一个文件叫hosts,其中就有127.0.0.1和localhost的映射关系。

实际上这个hosts文件非常好用。

1、如果我们知道了新浪的ip地址,那么我们就添加一条新浪的ip和www.sina.com.cn的映射,那么访问新浪的时候就不用访问DNS服务器(因为计算机先访问这个文件看看有没有对应关系才访问DNS服务器),这样会快很多!

2、如果有恶意网站,那么添加一条127.0.0.1和这个恶意网址的关系,那么相当于这个网站被屏蔽了。


你可能感兴趣的:(技术日记)