黑马程序员 网络编程

来源:互联网 发布:动态表单数据库设计 编辑:程序博客网 时间:2024/06/05 11:24

------- android培训、java培训、java学习型技术博客、期待与您交流! ----------

网络编程

IP:当我们一台机器将信息发送到另一台机器,要先找到对方的IP,IP是计算机的地址 

端口:,然后将数据发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这个数字,就叫做端口,逻辑端口

协议:它是一种通信规则,国际组织定义了通用协议TCP/IP.

IP编程

IPInternet Protocol(网络之间互连的协议)的缩写,IP地址在java中也有自己的类,这个类在java.net包下是一个InetAddress类,这个类是一个无构造函数的一个类。

这个类常用的方法:

a)      static   InetAddress getLocalHost():返回本地主机。获取引用对象

b)      String   getHostName()  : 获取此IP 地址的主机名。

c)      String   getHostAddress() : 回IP 地址字符串(以文本表现形式)。//常用

d)      static    InetAddress getByName(String host): 在给定主机名的情况下确定主机的IP 地址。获取引用对象

e)      String   toString():将此 IP 地址转换为String

UDP和TCP特点

UDP特点

a)  面向无连接,即将数据及源和目的封装成数据包,不需要建立连接

b)  数据包限制,即每个数据包的大小限制在64k。

c)  不可靠协议,因为无连接,是不可靠协议

d)  速度快,因为不需要建立连接,速度很快,但容易丢包

TCP特点:

a)  面向连接,建立连接,形成传输数据的同道

b)  大数据传输,在连接中进行大数据传输,不用封装包

c)  可靠协议,通过三次握手完成连接

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

UDP网络编程

UDP发送端

Socket是一种现实中的插槽或者插座的意思,为网络服务提供的一种机制

当我们发送一段信息时,UDP的发送过程:

1.       建立UDPSocket服务。

使用的是DatagramSocket对象

2.       提供数据,并将数据封装到数据包中

a)    数据:是一个byte[]数组存放的内容

b)    数据包,使用的是DatagramPacket对象,

构造方法是DatagramPacket(byte[] byf,intlength,InetArreess address,int port)

即(数据,数据的长度,要发送的主机(主机或IP),端口号)

3.       通过Socket服务的发送功能,将数据包发出去

DatagramSocket的send(数据包对象)方法

4.       关闭资源

我们再用代码来演示下具体过程:

importjava.net.*;

class  UPDSend

{

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

         {

                  //过程1:创建一个UDP服务,通过DatagramSocket对象

                   DatagramSocket ds=newDatagramSocket();

                   //过程2-1:创建一组数据

                   byte[] buf="Hello worldwelcome".getBytes();

                   //过程2-2:创建数据包,将数据封装到数据包里,通过DatagramPacket对象

                   DatagramPacket dp=

newDatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.100"),8080);

                   //过程3:发送数据,使用DatagramSocket的send()方法。

                            ds.send(dp);

         }

}

UDP接受端

当我们接受udp协议传输数据并处理数据时的过程:

1.       定义UDPSocket服务,通常会监听一个端口,其实就是给这个接受网络应用程序定义数字标识,方便与明确哪些数据过来,该应用程序可以处理

使用DatagramSocket的带参数的对象,此参数传入的一定要是端口号。

DatagramSocket(int port):创建数据报套接字并将其绑定到本地主机上的指定端口

2.       定义一个数据包,因为要存储接受到的字节数据,因为数据包对象中有更多功能可以提取字节数据中的不同数据信息

a)       数据:byte[] buf=new byte[1024];用来存储数据的空间

b)       数据包:DatagramPacket(byte[] buf, int length): 构造DatagramPacket,用来接收长度为 length 的数据包,即(数据空间,所接受的数据长度)

3.       通过Socket服务的received方法将收到的数据存入以定义好的数据包中。

DatagramSocket的receive方法.

4.       通过数据包对象的特有功能,将这些不同的数据取出,打印在目的地。

常用的方法:

a)  byte[] getData() :返回数据缓冲区,即接受的数据

b)  InetAddress getAddress():返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的

c)  int getLength():返回将要发送或接收到的数据的长度。

d)  int getPort() :返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。

5.       关闭资源

ds.close();

我们用代码基本演示如下:

import java.net.*;

class UDPRec

{

public staticvoid main(String[] args) throws Exception

{

           //过程1:创建一个UDP服务,建立一个绑定主机的端口

           DatagramSocketds=new DatagramSocket(8080);

          

           //过程2-1:定义一个要存储数据的空间

           byte[]buf=new byte[1024];

           //过程2-2:定义一个数据包,存储接受数据,并将之封装成对象

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

 

           //过程3:通过Socket服务将已接受的数据存入到已定义好的数据包中

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

 

           //过程4:通过数据包DatagramPacket的特有功能,

           //将这些数据包取出来打印在控制台上

           Stringip=dp.getAddress().getHostAddress();//获得之际IP号

           Stringdata=new String(dp.getData(),0,dp.getLength());//接受数据,和数据长度

           intport=dp.getPort();//获得端口号

           System.out.println("ip"+ip+".....接受到的数据:"+data+"来自"+port+"端口号");

           //过程5:关闭资源,

           ds.close();

}

}

UDP的应用

我们现在将发送端和接收端写一个聊天小程序。这个程序分为两部分。一个是收数据的部分,一个是发数据的部分。这两个部分要同时执行。我们需要通过多线程技术。一个线程控制收,一个线程控制发。代码演示如下:

importjava.net.*;

importjava.io.*;

/*

发送线程

*/

classSend implements Runnable

{

         private DatagramSocket ds;

         public Send(DatagramSocket ds)

         {

                   this.ds=ds;

         }

         public void run()

         {

                   try

                   {

                            BufferedReader br=

                            newBufferedReader(new InputStreamReader(System.in));

                            String line=null;

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

                            {

                                      if ("886".equals(line))

                                               break;

                                     byte[]buf=line.getBytes();

                                               DatagramPacketdp=newDatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),8080);

                                     ds.send(dp);

                            }

                            br.close();

                   }

                   catch (Exception e)

                   {

                            throw newRuntimeException("发送失败");

                   }               

         }

}

/*

接受线程

*/

classRec implements Runnable

{

         private DatagramSocket ds;

         public Rec(DatagramSocket ds)

         {

                   this.ds=ds;

         }

         public void run()

         {

                   try

                   {

                            while (true)

                            {

                                     byte[]buf=new byte[1024];

                                      DatagramPacket dp=newDatagramPacket(buf,buf.length);

                                     ds.receive(dp);

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

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

                                     System.out.println("来自"+ip+"的会话:"+data);

                            }

                   }

                   catch (Exception e)

                   {

                     throw new RuntimeException("接受失败");

                   }

         }

}

classChatDemo

{

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

         {

                   DatagramSocket send=newDatagramSocket();

                   DatagramSocket rec=newDatagramSocket(8080);

                  

                   //开启发送线程

                   new Thread(newSend(send)).start();

                   //开启接受线程

                   new Thread(newRec(rec)).start();

         }

}

 

TCP网络编程

      TCP分为客户端和服务端,客户端,它们分别对应的对象是Socket,服务器端对应的是SeverSocket

 

TCP客户端

      通过查阅Socket对象,发现在该对象建立时,就可以去连接指定主机,因为TCP是面向连接的,所以在建立Socket服务时,就要有服务器端存在,并连接成功。形成通路后,在该通道进行数据的传输。

 

步骤:

1.建立Socket服务,并指定要连接的主机和端口。

2.获取Socket流中的输出流,将数据写到该流中。通过网络发送给服务端

3.关闭客户端资源

代码演示

import java.net.*;

import java.io.*;

class TCPClient

{

         publicstatic void main(String[] args) throws Exception

         {

                   //创建客户端,指定目的主机和端口号

                   Socketsoc=new Socket("192.168.1.100",10001);

                  

                   //为了发送数据,应该获取Socket流中的输出流

                   OutputStreamout=soc.getOutputStream();

                  

                   out.write("Helloworld welcome".getBytes());

 

                   soc.close();

         }

}

 

TCP服务端

定义端点接收数据并打印在控制台上。

 

步骤:

1.  建立服务器端的Socket服务,即SeverSocket对象,并监听一个端口

2.  获取连接过来的客户端对象。

     通过ServerSocket的accpet方法。没有连接就会等,所以这个方法是阻塞式的。

3. 客户端如果发过来数据,那么服务器端要使用对应的客户端对象,并获取到该客户端的读取流来读取发过来的数据。并打印在控制台上

4.  关闭服务端(可选的,一般不会关)。

 

代码演示:

import java.io.*;

import java.net.*;

class TCPServer

{

public staticvoid main(String[] args) throws Exception

{

           //创建服务器

           ServerSocketss=new ServerSocket(10001);         

           //使用SeverSocket方法来接受来自于端口的信息

           Sockets=ss.accept();         

           //获得信息来源的主机

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

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

           //获得客户端的信息

           InputStreamin=s.getInputStream();        

           byte[]buf=new byte[1024];

           intlen=in.read(buf);

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

           System.out.println(data);

           ss.close();

}

}

客户端和服务器器交互

客户端和服务器的原理是:两者都互相在发送信息,即跟人与人打电话一样,有听筒和话筒。

我们用代码来演示下:
import java.io.*;

importjava.net.*;

/*

客户端

*/

classClient

{

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

         {

                   Socket s=newSocket("192.168.1.100",10009);

                  

                   //发送给服务器消息

                   OutputStreamout=s.getOutputStream();

                   out.write("来自客户端消息:我来了".getBytes());

 

                   //接受服务器消息

                   InputStream in=s.getInputStream();

                   byte[] buf=new byte[1024];

                   int len=in.read(buf);

                   String data=newString(buf,0,len);

                   System.out.println(data);

                   s.close();

         }

}

/*

服务器端

*/

classServer

{

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

         {

                   ServerSocket ss=newServerSocket(10009);

                   System.out.println("等待客户端......");

                   Socket s=ss.accept();

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

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

                   //接受客户端消息

                   InputStreamin=s.getInputStream();

                   byte[] buf=new byte[1024];

                   int len=in.read(buf);

                   String data=newString(buf,0,len);

                   System.out.println(data);               

                   //发送给客户端消息

                   OutputStreamout=s.getOutputStream()

                    out.write("来自服务器端的消息:收到".getBytes());             

                   ss.close();

         }

}

网络编程与IO流交互应用:

应用1建立一个文本转换器,客户端给服务器端发送文本,服务端会将文本转换成大写再返回给客户端。而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束。服务器也结束。

分析思路:

客户端:

既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作的规律来思考。

源:键盘录入

目的:网络设备,网络输出流

而且操作的是文本数据,可以选择字符流。


服务端:

源:Socket读取流

目的:Socket输出流

都是文本,装饰

代码演示如下:

importjava.net.*;

importjava.io.*;

/*

客户端

*/

classTransClient

{

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

         {

                   System.out.println("请输入你要转换的字母:");

                   Socket s=newSocket("192.168.1.100",9999);

                   //从键盘上读取字符

                   BufferedReader br=

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

                   //从服务器端接受的信息

                   BufferedReader in=

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

 

                   //第一种方式;向服务器端发送信息

                   //BufferedWriter out=

                   //new BufferedWriter(newOutputStreamWriter(s.getOutputStream()));

 

                   //第二种种方式;向服务器端发送信息

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

 

                   String line=null;

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

                   {

                            if("over".equals(line))

                                     break;

                            pw.println(line);

                            //out.write(line);

                            //out.newLine();//必须换行,否则一直等待

                            //out.flush();//必须刷新

                           

                            Stringstr=in.readLine(); 

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

                   }

 

                   br.close();

                   s.close();

         }

}

/*

服务器端

*/

classTransServer

{

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

         {

                   ServerSocket ss=newServerSocket(9999);

                   System.out.println("等待客户端。。。。。");

                   Socket s=ss.accept();

                    

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

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

 

                   //第一种方式:向客户端发送信息

                   //BufferedWriter out=

                   //new BufferedWriter(newOutputStreamWriter(s.getOutputStream()));

 

                   //第二种方式:向客户端发送信息

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

                   //从客户端接受信息

                   BufferedReader in=

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

 

                   String line=null;

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

                   {

                            System.out.println(line);

                            pw.println(line.toUpperCase());

 

                            //out.write(line.toUpperCase());//转换大写

                            //out.newLine();//必须换行

                            //out.flush();//必须刷新

                   }

                  

                   ss.close();

         }

}

 

该例子出现的问题.

现象:客户端和服务端都在莫名的等待、

原因:因为客户端和服务端都有阻塞式方法。,这些方法没有读到结束标记,那么就一直等待,而导致两端,都在等待。我们最好在向对方发送消息时,使用PrintWriter对象,因为它可以接受字符和字节,方法println()有自动刷新换行功能。

 

应用2:上传文件,建立一个服务器,并给给服务器上传一个文件。

importjava.io.*;

importjava.net.*;

class  TestClient

{

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

         {

                   Socket s=newSocket("192.168.1.100",8888);

                   BufferedReader br=newBufferedReader(new FileReader("IPDemo.java"));

                   BufferedReader in=

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

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

 

                   String line=null;

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

                   {

                            out.println(line);

                   }

 

                   //out.println("over");//结束标记1,否则一直读取

                   s.shutdownOutput();//结束标记2,否则一直读取

 

                   String news=in.readLine();

                   System.out.println(news);

 

                   br.close();

                   s.close();

         }

}

class  TestServer

{

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

         {

                   System.out.println("等待客户端.......");

                   ServerSocket ss=newServerSocket(8888);

                   Socket s=ss.accept();

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

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

        

                   BufferedReader in=

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

                   PrintWriter pw=newPrintWriter(new FileWriter("server.txt"),true);

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

                 

                   String line=null;

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

                   {

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

                                     //break;

                            pw.println(line);

                   }

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

 

                   pw.close();

                   ss.close();

         }

}

 

网络编程与多线程交互

我们发现上面例子服务端有个局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程,这时B客户端连接只有等待,因为服务端还没有处理完A客户端的请求,还没有循环回来执行下次accept方法,所以暂时获取不到B客户端对象。那么为了可以让多个客户端同时并发访问服务端,那么服务端最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求。

如何去定义线程呢?只要明确了每一个客户端要在服务端执行的代码即可,将该代码存入run方法中、

 

应用1:客户端上传MP3文件的代码演示:

importjava.io.*;

importjava.net.*;

/*

客户端

*/

class  Mp3Clinet

{

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

         {

                   //获得客户端的ip地址

                   StringhostIp=InetAddress.getLocalHost().getHostAddress();

                   Socket s=newSocket(hostIp,900);

                   //判断输入的长度是否为1

                   if (args.length!=1)

                   {

                            System.out.println("你还没有选择你要上传图片的路径");

                            return;

                   }

                   File file=new File(args[0]);

                   if(!(file.exists()||file.isFile())

                   {

                            System.out.println("你上传的文件不存在或者不是文件");

                            return;

                   }

                   //判断文件是否大于8m

                   if(file.length()>1024*1024*8)

                   {

                            System.out.println("对不起你上传的长度过大");

                            return;

                   }

                   if(!(file.getName().endsWith(".mp3")))

                   {

                            System.out.println("对不起,你上传的文件不是MP3格式,请重新选择");

                            return;

                   }

 

                   System.out.println("正在上传歌曲....");

 

                   BufferedInputStream bis=

                   new BufferedInputStream(newFileInputStream(file));

                  

                   BufferedOutputStream out=

                   newBufferedOutputStream(s.getOutputStream());

                   BufferedReader in=

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

                  

                   byte[] buf=new byte[1024];

                   int len=0;

                   while((len=bis.read(buf))!=-1)

                   {

                            out.write(buf,0,len);

                   }

                   s.shutdownOutput();//结束标记

                  

                   String news=in.readLine();

                   System.out.println(news);

 

                   bis.close();

                   s.close();

         }

}

/*

线程

*/

class  Mp3Server

{

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

         {

                   ServerSocket ss=newServerSocket(900);

                   while (true)

                   {

                            System.out.println("等待客户端.......");

                            Sockets=ss.accept();

 

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

                   }

         }

}

/*

服务器端线程

*/

classThreadMp3 implements Runnable

{

         private Socket s;

         ThreadMp3(Socket s)

         {

                   this.s=s;

         }

         public void run()

         {

                   int count=0;

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

                   try

                   {

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

                            File file=newFile(ip+".mp3");

                            while(file.exists())

                            {

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

                            }

                            BufferedOutputStreambos=

                            newBufferedOutputStream(new FileOutputStream(file));

                            BufferedInputStreamin=new BufferedInputStream(s.getInputStream());

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

 

                            byte[] buf=newbyte[1024];

                            int len=0;

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

                            {

                                     bos.write(buf,0,len);

                            }

 

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

 

                            bos.close();

                   }

                   catch (Exception e)

                   {

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

                   }       

         }

}

应用2:客户端并发登录

题目要求:客户端通过键盘录入用户名,服务端对这个用户名进行校验,如果该用户存在,在服务端显示***,已登录。并在客户端显示***,欢迎光临。

如果改用不存在,在服务端显示***,尝试登录,并在客户端显示***,该用户不存在。最多就登录三次。

代码演示如下:

importjava.net.*;

importjava.io.*;

 

classUserClient

{

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

         {

                   Socket s=newSocket("192.168.1.100",1111);

                   BufferedReader br=

                   new BufferedReader(newInputStreamReader(System.in));

                   BufferedReader in=

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

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

 

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

                   {       

 

                            String name=br.readLine();

                            //用户名为空跳出循环,用于登录成功后会出现的问题

                            if (name==null)

                                     break;

                            out.println(name);

 

                             String news=in.readLine();

                             System.out.println(news);

                             //如果服务器返回的有欢迎字就跳出循环

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

                                     break;

                   }

 

                   br.close();

                   s.close();

         }

}

classUserThread implements Runnable

{

         private Socket s;

         UserThread(Socket s)

         {

                   this.s=s;

         }

         public void run()

         {

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

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

                   try

                   { 

                            for (inti=0;i<3;i++ )

                            {

                                     BufferedReaderbr=new BufferedReader(new FileReader("user.txt"));

                                     BufferedReaderin=

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

 

                                     Stringname=in.readLine();

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

                                    

 

                                     Stringinfo=null;

                                     booleanflag=false;//标记

                                     while((info=br.readLine())!=null)

                                     {       

                                               //如果用户名和信息一样

                                               if(name.equals(info)){

                                                        flag=true;

                                                        break;

                                               }

                                     }

                                     if (flag)

                                     {

                                               System.out.println(ip+"登录成功");

                                               out.println("欢迎"+name+"进入系统");

                                               break;

                                     }else

                                               {

                                                        out.println("你好,此用户名不存在,登录失败");

                                                        System.out.println(ip+"正在尝试登录。。。。");

                                               }

                            }

                            s.close();

                   }

                   catch (Exception e)

                   {

                            throw newRuntimeException("登录异常");

                   }

         }

}

class  UserServer

{

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

         {

                   ServerSocket ss=newServerSocket(1111);

                   System.out.println("等待客户端.......");

                   while(true){

                   Socket s=ss.accept();

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

                   }

         }

}

 

网络支持

网络的一个常用对象就是URL,类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。

它的常用方法是什么呢?

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

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

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

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

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

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

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

URLConnection的一个方法:InputStreamgetInputStream():返回从此打开的连接读取的输入流。

我们简单代码演示下:

classURLDemo

{

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

         {

                   URL u=new URL("http://www.baidu.com");

                   //URL u=newURL("HTTP://192.168.1.100:7777/myweb/1.html?name=haha&age=13");

                   System.out.println("协议:"+u.getProtocol());

                   System.out.println("主机地址:"+u.getHost());

                   System.out.println("端口号:"+u.getPort());

                   System.out.println("文件路径:"+u.getPath());

                   System.out.println("文件名:"+u.getFile());

                   System.out.println("额外信息:"+u.getQuery());

                  

                   URLConnectionco=u.openConnection();//连接指定的URL

                   System.out.println(co);

                  

                   InputStreamin=co.getInputStream();

                   byte[] buf=new byte[1024];

                   int len=0;

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

                   {

                            System.out.println(newString(buf,0,len));//打印网页的HTML文本

                   }

         }

原创粉丝点击