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

Java网络编程详解

2014-11-20 20:52 274 查看
什么是IP协议?IP协议是用于报文交换网络的一种面向数据的协议,它定义了寻址方法和数据报的封装格式,任务是根据主机和目的主机的地址传送数据。

什么是TCP协议?TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于IP的传输层协议。在发送数据之前,必须与对方建立可靠的连接。一个TCP连接必须经过三次“会话”才能建立起来,也就是“三次握手”。

TCP三次握手过程:

主机A 通过向主机B发送一个含有同步序列号(SYN)的标志位的数据段给主机B、向主机B 请求建立连接,通过这个数据段,主机A 告诉主机B 两件事:我想要和你通信。
主机B 收到主机A 的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机A,也告诉主机A 两件事:我已经收到你的请求,你可以传输数据了。
主机A 收到这个数据段后,再发送一个确认应答,确认已收到主机B 的数据段:我已收到回复,我现在要开始传输实际数据了。

这样3次握手就完成了,主机A和主机B 就可以传输数据了。

“三次握手”过程中始终没有应用层的数据交换,SYN这个标志位只有在CP建产连接时才会被置1,握手完成后SYN标志位被置0。

什么是UDP协议?UDP协议(User Datagram Protocol,用户数据报协议)是无连接、不可靠、面向事务的协议。传输数据之前,源端和终端不需要建立连接,当需要传送数据时,就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制。

TCP与UDP的区别

TCP协议面向连接,UDP协议面向非连接;
TCP协议传输速度慢,UDP协议传输速度快;
TCP有丢包重传机制,UDP没有;
TCP协议保证数据正确性,UDP协议可能丢包;

什么是Socket?Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是一组把复杂的TCP/IP协议族隐藏在后面的接口,用于描述IP地址和端口,应用程序通常通过套接字向网络发出请求或者应答网络请求。对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

下面以C/S结构为基础来介绍TCP编程的实现。

客户端(Client)是指首先发起连接的程序,主要有三个步骤:

建立连接:在建立连接时需要指定服务器的IP地址和端口号;
交换数据:按照请求响应模型进行,由客户端发送一个请求数据到服务器端,服务器端反馈一个响应数据给客户端,如果客户端不发送请求,服务器端则不响应。
关闭连接:释放程序占用的端口、内存等系统资源。

服务器端(Server)是指被动等待连接的程序,主要有四个步骤:

监听端口:这个端口就是服务器端开放给客户端的端口,服务器端程序运行的本地计算机的IP地址就是服务器端程序的IP地址。
获得连接:这个连接包含客户端的信息,例如客户端IP地址。一般在服务器端编程中,当获得连接时,需要开启专门的线程处理该连接,每个连接都由独立的线程实现,这样服务器就能同时处理多个客户端。
交换数据:接收到客户端发送过来的数据,然后进行处理,再把处理结果发送给客户端。简单来说,就是先接收再发送。
关闭连接:释放程序占用的端口、内存等系统资源。

TCP服务器端事例代码:

public class Server
{
   public static int port = 10000;      // 服务器端的端口号

   public static void main(String[] args)
   {
      ServerSocket serverSocket = null;
      Socket socket = null;

      try
      {
         serverSocket = new ServerSocket(port);
         System.out.println("服务器已启动,等待接受消息中 ...");

         while(true)// 循环等待接受客户端消息
         {
            socket = serverSocket.accept();// 获得连接
            new HandleThread(socket);// 启动线程
         }
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      finally
      {
         try
         {
            serverSocket.close();
         }
         catch(Exception e)
         {
            e.printStackTrace();
         }
      }
   }
}

/**
 * 服务器端线程,同时处理多个客户端
 */
class HandleThread extends Thread
{
   Socket       socket = null;
   InputStream  is     = null;
   OutputStream os     = null;

   public HandleThread(Socket socket)
   {
      this.socket = socket;
      start(); // 启动线程
   }

   public void run()
   {
      byte[] b = new byte[1024];

      try
      {
         // 初始化流
         os = socket.getOutputStream();
         is = socket.getInputStream();
         int n = is.read(b);// 读取数据
         String answer = "Hello, I'm server!";
         os.write(answer.getBytes());// 反馈数据
         System.out.println("客户端发送内容为(" + new SimpleDateFormat("hh:mm:ss").format(new Date())
               + "):" + new String(b, 0, n));// 输出

      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      finally
      {
         close();
      }
   }

   /**
    * 关闭流和连接
    */
   private void close()
   {
      try
      {
         os.close();
         is.close();
         socket.close();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
}
ServerSocket的accept方法是阻塞式的,它会等待客户端与之建立连接。Socket的getInputStream方式也是阻塞的,如果从输入流中没有读取到数据程序会一直在那里不动,直到客户端往Socket的输出流中写入了数据,或关闭了Socket的输出流,对于客户端的Socket也是同样如此。

TCP客户端代码:

public class Client
{
   public static String serverIP = "127.0.0.1"; // 服务器端IP地址
   public static int    port     = 10000;      // 服务器端端口号

   public static void main(String[] args)
   {
      Socket socket = null;
      InputStream is = null;
      OutputStream os = null;

      // 发送内容
      String data = "Hello, I'm client.";
      try
      {

         socket = new Socket(serverIP, port);// 建立连接
         os = socket.getOutputStream();// 初始化流
         is = socket.getInputStream();
         byte[] b = new byte[1024];

         os.write(data.getBytes());// 发送数据
         int n = is.read(b);// 接收数据
         // 输出反馈数据
         System.out.println("服务器反馈(" + new SimpleDateFormat("hh:mm:ss").format(new Date()) + "):"
               + new String(b, 0, n));

      }
      catch(Exception e)
      {
         e.printStackTrace(); // 打印异常信息
      }
      finally
      {
         try
         {
            // 关闭流和连接
            is.close();
            os.close();
            socket.close();
         }
         catch(Exception e2)
         {
            e2.printStackTrace();
         }
      }
   }
}
运行服务器端程序,接着运行多次客户端程序,服务器端得到的消息是:

服务器已启动,等待接受消息中 ...
客户端发送内容为(09:10:12):Hello, I'm client.
客户端发送内容为(09:10:14):Hello, I'm client.
客户端发送内容为(09:10:18):Hello, I'm client.
客户端发送内容为(09:10:19):Hello, I'm client.
客户端发送内容为(09:10:20):Hello, I'm client.


UDP方式由于不需要建立专用的连接,对服务器的压力要比TCP小很多,但是同时传输不可靠,有可能发生数据丢失的情况。在Java API中,实现UDP方式主要由两个类实现:DatagramSocket和DatagramPacket。前者用于发送和接收数据,后者用于数据的封装(IP地址、端口号和数据内容)。

UDP服务器端代码:

public class UDPServer
{
   public static int port = 10000;      // 服务器端的端口号

   public static void main(String[] args)
   {
      testUDPServer();
   }

   public static void testUDPServer()
   {
      DatagramSocket ds = null; // 连接对象
      DatagramPacket receiveDp; // 接收数据包对象
      final int PORT = 10000; // 端口
      byte[] b = new byte[1024];
      receiveDp = new DatagramPacket(b, b.length);

      try
      {
         // 建立连接,监听端口
         ds = new DatagramSocket(PORT);
         System.out.println("服务器端已启动:");
         while(true)
         {
            ds.receive(receiveDp);// 接收
            new HandleThread(ds, receiveDp);// 启动线程处理数据包
         }
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      finally
      {
         try
         {
            ds.close();
         }
         catch(Exception e)
         {}
      }
   }
}

/**
 * 逻辑处理线程
 */
class HandleThread extends Thread
{
   /** 连接对象 */
   DatagramSocket ds = null;
   /** 接收到的数据包 */
   DatagramPacket dp = null;

   public HandleThread(DatagramSocket ds, DatagramPacket dp)
   {
      this.ds = ds;
      this.dp = dp;
      start(); // 启动线程
   }

   public void run()
   {
      try
      {
         byte[] data = dp.getData();// 获得缓冲数组
         int len = dp.getLength();// 获得有效数据长度
         InetAddress clientAddress = dp.getAddress();// 客户端IP
         int clientPort = dp.getPort();// 客户端端口
         
         // 输出
         System.out.println("客户端IP:" + clientAddress.getHostAddress());
         System.out.println("客户端端口号:" + clientPort);
         System.out.println("客户端发送内容:" + new String(data, 0, len));
         
         // 反馈到客户端
         byte[] b = new SimpleDateFormat("hh:mm:ss").format(new Date()).getBytes();
         DatagramPacket sendDp = new DatagramPacket(b, b.length, clientAddress, clientPort);
         ds.send(sendDp);// 发送
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
}
和TCP方式的网络编程类似,服务器端的receive方法也是阻塞方法,如果客户端不发送数据,则程序会在该方法处阻塞。当客户端发送数据到达服务器端时,则接收客户端发送过来的数据,然后将客户端发送的数据内容读取出来进行处理。从客户端发送过来的数据包中可以读取出客户端的IP以及客户端端口号。最后关闭服务器端连接,释放占用的系统资源。

UDP客户端代码:

public class UDPClient
{
   public static String serverIP = "127.0.0.1"; // 服务器端IP地址
   public static int    port     = 10000;      // 服务器端端口号

   public static void main(String[] args)
   {
      DatagramSocket ds = null; // 连接对象
      DatagramPacket sendDp; // 发送数据包对象
      DatagramPacket receiveDp; // 接收数据包对象

      try
      {
         ds = new DatagramSocket();
         InetAddress address = InetAddress.getByName(serverIP);
         byte[] b = new byte[1024];
         receiveDp = new DatagramPacket(b, b.length);

         String content = new SimpleDateFormat("hh:mm:ss").format(new Date());

         byte[] data = content.getBytes();

         sendDp = new DatagramPacket(data, data.length, address, port);// 初始化发送包对象
         ds.send(sendDp);// 发送
         ds.receive(receiveDp);// 接收

         byte[] response = receiveDp.getData();// 读取反馈内容,并输出
         int len = receiveDp.getLength();
         String s = new String(response, 0, len);
         System.out.println("服务器端回答为:" + s);

      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      finally
      {
         try
         {
            ds.close();
         }
         catch(Exception e)
         {

         }
      }
   }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: