java 网络编程上

网络编程的基础知识

本地回路地址:127.0.0.1.

同一计算机中不能有两个应用程序有相同的端口号,端口号范围065535,01023之间的端口数是用于一些知名的网络服务与应用。

Socket 网络驱动层 提供给应用程序编程的接口和一种机制,在应用程序中创建,通过绑定机制与驱动程序建立关系,并告知自己所对应的ipport,为应用程序的“码头”。在传送方,只需将数据放到这个码头,就可以了,剩下的由驱动程序完成。在接收方,也有自己的驱动程序接收数据,然后传给“码头”。接收方会不断查询“码头”。

Socket数据发送过程:

 

Socket数据接受过程:
   

 

DatagramSocket类用于UDP通信

ServerSocket 类用于TCP通信的服务器端。(接受客户端的连接)

Socket类用于TCP通信的服务器和客户端(专门用于数据传输)

如果在创建Socket的时候,没有指定端口号,系统会为它分配一个未使用的端口号。

如果在创建Socket的时候,没有指定IP地址,那么底层驱动程序会从计算机上选择任一个IP地址作为发送源的IP。若一个计算机只有一个IP,则不需指定了。

 

UDP编程

DatagramSocket

1、构造函数:

public DatagramSocket()  不指明IP地址和端口号,用于UDP程序不先接收数据而是先发送数据时,此时系统会为它分配一个未使用的端口号。

public DatagramSocket(int port)   用于UDP程序先接受数据时

public DatagramSocket(int port,InetAddress laddr)  用于UDP程序运行的主机中有多个IP地址时,需指明UDP程序接受和发送数据使用的IP地址

2 close方法 通知驱动程序释放相应的资源,原程序所用的端口号可以分配给其他的程序

3 send(DatagrmPacket p)方法用于发送UDP数据包

4 receive(DatagramPacket p)方法接受发送UDP数据包

 

DatagramPacket

如果把DatagramSocket比作创建的港口码头,那么DatagramPacket就是发送和接收数据的集装箱。

主要构造函数:

public DatagramPacket(byte[] buf,int length) 两个参数分别指明了数据包的缓冲区及缓冲区的大小。此方法用于接收数据时。

public DatagramPacket(byte[] buf,int length,InetAddress address,int port)前两个参数意义同上,后两个参数分别指明了接收数据包的目标程序IP地址及端口。此方法用于发送数据时。并且buf中必须含有要发送的数据。

当用于接收数据DatagramPacke对象接受到网络上发送过来的数据以后,对方的IP地址及端口号也是包含在DatagramPacke对象当中的,使用DatagramPackegetInetAddressgetPort方法就可以获得发送方的端口号和IP地址。

当创建接收数据包的DatagramPacke对象时,因为无法预知接受的数据大小,都是在程序中预先定义一个相对足够大的字节数组作为缓冲区,传递给DatagramPacke的构造函数。getData方法就是返回DatagramPacke对象的字节数组缓冲区,getLength方法返回DatagramPacke对象实际接收到的数据的长度。

另外只有UDP程序才能接受和发送广播数据(IP地址的主机号每位都设为1)

InetAddress

InetAddress是用于表示计算机IP地址的一个类。

getByName方法 :是InetAddress的一个静态方法,可以根据网络地址字符串(如“www.it325.org”)来返回相应的InetAddress实例对象。

getHostAddress方法:可以返回用点分割的数字表示的IP地址。(返回字符串格式)

 

UDP接受程序调用receive方法接收数据时,如果Socket缓冲区中没有数据到达,该方法会一直处于堵塞状态,直到有数据到达。

UDP发送程序调用send方法发送数据时,当接受程序没有工作时仍发送,会导致数据流失。因此应先让UDP接受程序运行。

 

在命令行中输入start,会产生一个新的命令行窗口,该窗口完全继承原窗口的环境。

变成举例:

import java.net.*;

public class UdpSend {

       public static void main(String[] args) {

              DatagramSocket ds=null;

              String str="hello java";//要发送的数据

              try

              {

                     ds=new DatagramSocket();

                     ds.send(new DatagramPacket(str.getBytes(),

                            str.length(),InetAddress.getByName("127.0.0.1"),3000));

              }

              catch(Exception e)

              {

                    

              }

              ds.close();

             

       }

 

}

 

import java.net.*;

public class UdpReveive {

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

              DatagramSocket ds=new DatagramSocket(3000);

              byte[] buf=new byte[1024];

              DatagramPacket dp=new DatagramPacket(buf,1024);

              ds.receive(dp);

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

              System.out.println(str+" from: "+dp.getAddress().getHostAddress()+

                                       " "+dp.getPort() );

              ds.close();

             

       }

 

}

 

UDP小聊天程序

import java.awt.*;

import java.awt.event.*;

import java.net.*;

 

public class Chat extends Frame{

      

       List lst=new List(6);

       //参数表示列表框中最多显示6行,超过6行用滚动条表示。

       TextField tfIP=new TextField(15);

       TextField tfData=new TextField(20);

       DatagramSocket ds=null;

      

    public Chat(String s)

    {

           super(s);

           try

           {

                  ds=new DatagramSocket(3000);

           }

           catch(Exception e)

           {

                  e.printStackTrace();

           }

         this.add(lst,"Center"); Panel p=new Panel();

           p.setLayout(new BorderLayout());p.add(tfIP,"West"); p.add(tfData,"East");

           this.add(p,"South"); this.setSize(300,350); this.setResizable(false);

         new Thread(new Runnable()

           /*在一个新的线程中启动接收过程,因为在接收不到时会堵塞,

             可以避免整个程序堵塞。*/

           {

                  public void run()

                  {

                         byte[] buf=new byte[1024];

                         DatagramPacket dp=new DatagramPacket(buf,1024);

                         while(true)

                         {

                                try

                                {

                                       ds.receive(dp);

                                       String str=new String(buf,0,dp.getLength());

                                       String strIP=dp.getAddress().getHostAddress();

                                       lst.add(str+" from "+strIP+":"+dp.getPort(), 0);

                       /*第二个参数表示插入的位置在第一行,无此参数则表示新加入的在

                                      * 最下面。*/

                            }

                                catch(Exception e)

                                {

                                       if(!ds.isClosed())

                                              e.printStackTrace();

                                }

                         }

                  }

           }).start();

           tfData.addActionListener(new ActionListener()

           {

                     public void actionPerformed(ActionEvent arg0) {

                            //当按下回车时传递信息

                            try

                            {

                                    DatagramPacketdp=newDatagramPacket(tfData.getText().getBytes(),

                                       tfData.getText().getBytes().length,InetAddress.getByName(tfIP.getText()),3000);    

                                   ds.send(dp);

                            }

                            catch(Exception e)

                            {

                                   e.printStackTrace();

                            }

                            tfData.setText("");

                     }

          });

          

           addWindowListener(new WindowAdapter()

           {

                  public void windowClosing(WindowEvent e)

                  {

                         dispose();

                         System.exit(0);

                  }

           });

    }

       public static void main(String[] args) {

              Chat chat=new Chat("聊天工具");

              chat.setVisible(true);

       }

}

 

 

TCP编程

TCP网络程序的工作原理

 

利用UDP进行通信的两个进程是平等的,没有主从之分,它们完全可以是同一个程序的两个运行实例。而利用UDP进行通信的两个程序是不平等的,一个是服务程序(Server Socket),一个是客户程序(Client Socket)。

TCP客户端程序的与TCP服务器程序的交互过程:

1)服务器建立一个ServerSocket类的实例,然后调用accept方法等待客户的连接。

2)客户端程序创建一个Socket并请求与服务器建立连接。

3)服务器接受客户的连接请求,并创建一个Socket与该客户建立连接。

4)建立了两个连接的两个Socket在一个单独的线程(由服务器程序创建)上对话。

5)服务器开始等待新的连接请求,当新的连接请求到达时,重复步骤(2)到步骤(5)的过程。

 

ServerSocket

构造函数:

——public ServerSocket():没有与任何的端口号绑定,该ServerSocket不能直接使用,还需要调用bind方法才能完成其他构造函数所完成的功能。

——public ServerSocket(int port):指定了一个端口号,这个端口号若为0,系统就会自动为它分配一个未被使用的端口号。作为服务程序最好先指定所用的端口号。

——public ServerSocket(int port ,int backlog):第二参数用于指定在服务器很忙的时候,可以保持的等待连接的客户数量。对于第二个构造函数没有指定的情况。默认的是50.

——public ServerSocket(int port ,int backlog,InetAddress binAddr):第三个参数表示ServerSocket所绑定的IP地址,这适合计算机上有多个IP的情况。

close方法:关闭程序,释放资源。

accept方法:该方法会返回一个由户端程序创建并客户建立连接的Socket对象。在没有客户请求的情况下,该方法处于堵塞状态。

 

Socket

——public Socke():不与任何服务器建立连接,不被直接使用,需要调用Socke类的connect方法,才能完成其他构造函数创建的Socket类所完成的功能。如果想要用一个Socket去轮询几个ServerSocket,可用该方法,在创建Socket对象后,再用connect方法去轮询ServerSocket服务器。

——public Socke(String host,int port)

——public Socke(InetAddress address,int port)

第二个和第三个构造函数多创建的Socket对象,会根据所指明的IP和端口号去连接服务器,第二个构造函数接受字符串格式的IP地址,第三个构造函数接受InetAddress对象格式的IP地址。

——public Socke(String host ,int port, InetAddress localAddr,int localPort)

——public Socke(InetAddress address,int port,InetAddress localAddr,int localPort)

第四个和第五个构造函数在第二个和第三个构造函数的基础上,还指明了本地的IP和程序所用的端口号,多用于本地有多个IP的时候。

 

当请求服务器服务后,服务器端会返回一个Socket对象与客户端的Socket建立连接。而这个服务器端的Socket对象,是不能用构造函数来创建的,而是由ServerSocketaccept方法返回的

Socket类的getInputStreamgetOutputStream方法

当服务器端Socket和客户端的Socket建立连接后,它们会以网络字节流的形式进行数据交换。

简单的服务器程序:

import java.net.*;

import java.io.*;

public class TcpServer {

       public static void main(String[] args) {

              try

              {

                     ServerSocket ss=new ServerSocket(8001);

                     Socket s=ss.accept();

                     InputStream ips=s.getInputStream();

                     OutputStream ops=s.getOutputStream();

                     ops.write("Welcome".getBytes());

                     BufferedReader br=new BufferedReader(new InputStreamReader(ips));

                     System.out.println(new String(br.readLine()));

                    

                     ips.close();ops.close();s.close();ss.close();

              }

              catch(Exception e)

              {

                     e.printStackTrace();

              }

       }

}

 

一个较完善的TCP服务器模型

import java.net.*;

public class ReverseServer {

       public static void main(String[] args) {

              try

              {

                     ServerSocket ss=new ServerSocket(8001);

                     while(true)

                     {

                            Socket s=ss.accept();

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

       /*要实现一个服务器与多个客户相连接,则要循环调用accept方法,并且与某个

客户进行数据交换的语句应在一个单独的进程中运行。

       *否则,在服务器与某客户进行连接时,无法执行accept方法与其他的客户连接

       *与某个客户进行数据交换的时候应有相应的Socket与之绑定 */

                    

                     }

                    

              }

              catch(Exception e)

              {

                     e.printStackTrace();

              }

       }

}

 

import java.net.*;

import java.io.*;

public class Servicer implements Runnable{

      

       private Socket s;

      

       public Servicer(Socket s)

       {

              this.s=s;

       }

      

       public void run()

       {

              try

              {

                     InputStream ips=s.getInputStream();

                  OutputStream ops=s.getOutputStream();

                  BufferedReader br=new BufferedReader(

                                new InputStreamReader(ips));

                  PrintWriter pw=new PrintWriter(ops,true);

                  /*使用PrintWriter类的println方法可以将字符串输入到底层输出流

                   * 并且可以产生与具体平台相关的换行符,而PrintStream类的println

                   * 方法的换行符只是/n*/

                  while(true)

                  {

                         String strLine=br.readLine();

                         if(strLine.equals("quit"))

                         {

                                break;

                         }

                         String strEcho=new StringBuffer(strLine).toString();

                         pw.println(strLine+" --> "+strEcho);

                  }

                  br.close();

                  pw.close();

                  s.close();

              }

              catch(Exception e)

              {

                     e.printStackTrace();

              }

       }

 

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值