Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 349|回复: 0

[网络编程学习]java网络编程从入门到精通(13):Socket发送数据

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-11-6 00:04:14 | 显示全部楼层 |阅读模式
    网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的java类。通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据。在本文及后面的数篇文章中将详细讨论Socket类的使用,内容包括Socket类基础、各式各样的连接方式、get和set方法、连接过程中的超时以及关闭网络连接等。

         在本文中,我们将讨论使用Socket类的基本步骤和方法。一般网络客户端程序在连接服务程序时要进行以下三步操作。

    1. 连接服务器
    2. 发送和接收数据
    3. 关闭网络连接

    一、连接服务器
          在客户端可以通过两种方式来连接服务器,一种是通过IP的方式来连接服务器,而另外一种是通过域名方式来连接服务器。
       
      
       
       
         
       

         
       
      
        其实这两种方式从本质上来看是一种方式。在底层客户端都是通过IP来连接服务器的,但这两种方式有一定的差异,如果通过IP方式来连接服务端程序,客户端只简单地根据IP进行连接,如果通过域名来连接服务器,客户端必须通过DNS将域名解析成IP,然后再根据这个IP来进行连接。

          在很多程序设计语言或开发工具中(如C/C++、Delphi)使用域名方式连接服务器时必须自己先将域名解析成IP,然后再通过IP进行连接,而在Java中已经将域名解析功能包含在了Socket类中,因此,我们只需象使用IP一样使用域名即可。

         通过Socket类连接服务器程序最常用的方法就是通过Socket类的构造函数将IP或域名以及端口号作为参数传入Socket类中。Socket类的构造函数有很多重载形式,在这一节只讨论其中最常用的一种形式:public Socket(String host, int port)。从这个构造函数的定义来看,只需要将IP或域名以及端口号直接传入构造函数即可。下面的代码是一个连接服务端程序的例子程序:

    1. package mysocket;
    2. import java.net.*;
    3. public class MyConnection
    4. {
    5.     public static void main(String[] args)
    6.     {
    7.         try
    8.         {
    9.             if (args.length > 0)
    10.             {
    11.                 Socket socket = new Socket(args[0], 80);
    12.                 System.out.println(args[0] + "已连接成功!");
    13.             }
    14.             else
    15.                 System.out.println("请指定IP或域名!");
    16.         }
    17.         catch (Exception e)
    18.         {
    19.             System.err.println("错误信息:" + e.getMessage());
    20.         }
    21.     }
    22. }
    复制代码
    在上面的中,通过命令行参数将IP或域名传入程序,然后通过Socket socket = new Socket(args[0], 80)连接通过命令行参数所指定的IP或域名的80端口。由于Socket类的构造函数在定义时使用了throws,因此,在调用Socket类的构造函数时,必须使用try…catch语句来捕捉错误,或者对main函数使用throws语句来抛出错误。

    测试正确的IP
    java mysocket.MyConnection 127.0.0.1
    输出结果:127.0.0.1已经连接成功!

    测试错误的IP
    java mysocket.MyConnection 10.10.10.10
    输出结果:错误信息:Connection timed out: connect
    注:10.10.10.10是一个并不存在的IP,如果这个IP在你的网络中存在,请使用其它的不存在的IP。

    测试正确的域名
    java mysocket.MyConnection www.ptpress.com.cn
    输出结果:www.ptpress.com.cn已经连接成功!

    测试错误的域名
    java mysocket.MyConnection www.ptpress1.com.cn
    输出结果:错误信息:www.ptpress1.com.cn

    使用Socket类连接服务器可以判断一台主机有哪些端口被打开。下面的代码是一个扫描本机有哪些端口被打开的程序。

    1. package mysocket;
    2. import java.net.*;
    3. public class MyConnection1 extends Thread
    4. {
    5.     private int minPort, maxPort;
    6.     public MyConnection1(int minPort, int maxPort)
    7.     {
    8.         this.minPort = minPort;
    9.         this.maxPort = maxPort;
    10.     }
    11.     public void run()
    12.     {
    13.         for (int i = minPort; i <= maxPort; i++)
    14.         {
    15.             try
    16.             {
    17.                 Socket socket = new Socket("127.0.0.1", i);
    18.                 System.out.println(String.valueOf(i) + ":ok");
    19.                 socket.close();
    20.             }
    21.             catch (Exception e)
    22.             {
    23.             }
    24.         }
    25.     }
    26.     public static void main(String[] args)
    27.     {
    28.         int minPort = Integer.parseInt(args[0]), maxPort = Integer.parseInt(args[1]);
    29.         int threadCount = Integer.parseInt(args[2]);
    30.         int portIncrement = ((maxPort - minPort + 1) / threadCount)
    31.                 + (((maxPort - minPort + 1) % threadCount) == 0 ? 0 : 1);
    32.         MyConnection1[] instances = new MyConnection1[threadCount];
    33.         for (int i = 0; i < threadCount; i++)
    34.         {
    35.             instances[i] = new MyConnection1(minPort + portIncrement * i, minPort
    36.                     + portIncrement - 1 + portIncrement * i);
    37.             instances[i].start();
    38.         }
    39.     }
    40. }
    复制代码
    上面代码通过一个指定的端口范围(如1至1000),并且利用多线程将这个端口范围分成不同的段进行扫描,这样可以大大提高扫描的效率。
    可通过如下命令行去运行例程4-2。
    java mysocket.MyConnection1 1000 3000 20

    二、发送和接收数据
        在Socket类中最重要的两个方法就是getInputStream和getOutputStream。这两个方法分别用来得到用于读取和写入数据的InputStream和OutputStream对象。在这里的InputStream读取的是服务器程序向客户端发送过来的数据,而OutputStream是客户端要向服务端程序发送的数据。

        在编写实际的网络客户端程序时,是使用getInputStream,还是使用getOutputStream,以及先使用谁后使用谁由具体的应用决定。如通过连接邮电出版社网站(www.ptpress.com.cn)的80端口(一般为HTTP协议所使用的默认端口),并且发送一个字符串,最后再读取从www.ptpress.com.cn返回的信息。

    1. package mysocket;
    2. import java.net.*;
    3. import java.io.*;
    4. public class MyConnection2
    5. {
    6.     public static void main(String[] args) throws Exception
    7.     {
    8.         Socket socket = new Socket("www.ptpress.com.cn", 80);
    9.         // 向服务端程序发送数据
    10.         OutputStream ops  = socket.getOutputStream();        
    11.         OutputStreamWriter opsw = new OutputStreamWriter(ops);
    12.         BufferedWriter bw = new BufferedWriter(opsw);
    13.         
    14.         bw.write("hello world

    15. ");
    16.         bw.flush();
    17.         
    18.         // 从服务端程序接收数据
    19.         InputStream ips = socket.getInputStream();
    20.         InputStreamReader ipsr = new InputStreamReader(ips);
    21.         BufferedReader br = new BufferedReader(ipsr);
    22.         String s = "";        
    23.         while((s = br.readLine()) != null)
    24.             System.out.println(s);        
    25.         socket.close();
    26.     }
    27. }
    复制代码
    在编写上面代码时要注意如下两点:
    1. 为了提高数据传输的效率,Socket类并没有在每次调用write方法后都进行数据传输,而是将这些要传输的数据写到一个缓冲区里(默认是8192个字节),然后通过flush方法将这个缓冲区里的数据一起发送出去,因此,bw.flush();是必须的。

    2. 在发送字符串时之所以在Hello World后加上 “

    ”,这是因为HTTP协议头是以“

    ”作为结束标志(HTTP协议的详细内容将在以后讲解),因此,通过在发送字符串后加入“

    ”,可以使服务端程序认为HTTP头已经结束,可以处理了。如果不加“

    ”,那么服务端程序将一直等待HTTP头的结束,也就是“

    ”。如果是这样,服务端程序就不会向客户端发送响应信息,而br.readLine()将因无法读以响应信息面被阻塞,直到连接超时。

    三、关闭网络连接
         到现在为止,我们对Socket类的基本使用方法已经有了初步的了解,但在Socket类处理完数据后,最合理的收尾方法是使用Socket类的close方法关闭网络连接。虽然在中已经使用了close方法,但使网络连接关闭的方法不仅仅只有close方法,下面就让我们看看Java在什么情况下可以使网络连接关闭。

    可以引起网络连接关闭的情况有以下4种:
    1. 直接调用Socket类的close方法。

    2. 只要Socket类的InputStream和OutputStream有一个关闭,网络连接自动关闭(必须通过调用InputStream和OutputStream的close方法关闭流,才能使网络可爱接自动关闭)。

    3. 在程序退出时网络连接自动关闭。

    4. 将Socket对象设为null或未关闭使用new Socket(…)建立新对象后,由JVM的垃圾回收器回收为Socket对象分配的内存空间后自动关闭网络连接。

        虽然这4种方法都可以达到同样的目的,但一个健壮的网络程序最好使用第1种或第2种方法关闭网络连接。这是因为第3种和第4种方法一般并不会马上关闭网络连接,如果是这样的话,对于某些应用程序,将会遗留大量无用的网络连接,这些网络连接会占用大量的系统资源。

        在Socket对象被关闭后,我们可以通过isClosed方法来判断某个Socket对象是否处于关闭状态。然而使用isClosed方法所返回的只是Socket对象的当前状态,也就是说,不管Socket对象是否曾经连接成功过,只要处于关闭状态,isClosed就返回true。如果只是建立一个未连接的Socket对象,isClose则会返回false。如下面的代码将输出false。
    Socket socket = new Socket();
    System.out.println(socket.isClosed());

       除了isClose方法,Socket类还有一个isConnected方法来判断Socket对象是否连接成功。看到这个名字,也许读者会产生误解。其实isConnected方法所判断的并不是Socket对象的当前连接状态,而是Socket对象是否曾经连接成功过,如果成功连接过,即使现在isClose返回true,isConnected仍然返回true。因此,要判断当前的Socket对象是否处于连接状态,必须同时使用isClose和isConnected方法,即只有当isClose返回false,isConnected返回true的时候Socket对象才处于连接状态。下面的代码演示了上述Socket对象的各种状态的产生过程。

    1. package mysocket;
    2. import java.net.*;
    3. public class MyCloseConnection
    4. {
    5.     public static void printState(Socket socket, String name)
    6.     {
    7.         System.out.println(name + ".isClosed():" + socket.isClosed());
    8.         System.out.println(name + ".isConnected():" + socket.isConnected());
    9.         if (socket.isClosed() == false && socket.isConnected() == true)
    10.             System.out.println(name + "处于连接状态!");
    11.         else
    12.             System.out.println(name + "处于非连接状态!");
    13.         System.out.println();
    14.     }
    15.     public static void main(String[] args) throws Exception
    16.     {
    17.         Socket socket1 = null, socket2 = null;
    18.         socket1 = new Socket("www.ptpress.com.cn", 80);
    19.         printState(socket1, "socket1");
    20.         socket1.getOutputStream().close();
    21.         printState(socket1, "socket1");
    22.         socket2 = new Socket();
    23.         printState(socket2, "socket2");
    24.         socket2.close();
    25.         printState(socket2, "socket2");
    26.     }
    27. }
    复制代码
    运行上面的代码后,将有如下的输出结果:
    socket1.isClosed():false
    socket1.isConnected():true
    socket1处于连接状态!
    socket1.isClosed():true
    socket1.isConnected():true
    socket1处于非连接状态!
    socket2.isClosed():false
    socket2.isConnected():false
    socket2处于非连接状态!
    socket2.isClosed():true
    socket2.isConnected():false
    socket2处于非连接状态!

    从输出结果可以看出,在socket1的OutputStream关闭后,socket1也自动关闭了。而在上面的代码我们可以看出,对于一个并未连接到服务端的Socket对象socket2,它的isClosed方法为false,而要想让socket2的isClosed方法返回true,必须使用socket2.close显示地调用close方法.

       虽然在大多数的时候可以直接使用Socket类或输入输出流的close方法关闭网络连接,但有时我们只希望关闭OutputStream或InputStream,而在关闭输入输出流的同时,并不关闭网络连接。这就需要用到Socket类的另外两个方法:shutdownInput和shutdownOutput,这两个方法只关闭相应的输入、输出流,而它们并没有同时关闭网络连接的功能。和isClosed、isConnected方法一样,Socket类也提供了两个方法来判断Socket对象的输入、输出流是否被关闭,这两个方法是isInputShutdown()和isOutputShutdown()。下面的代码演示了只关闭输入、输出流的过程:

    1. package mysocket;
    2. import java.net.*;
    3. public class MyCloseConnection1
    4. {
    5.     public static void printState(Socket socket)
    6.     {
    7.         System.out.println("isInputShutdown:" + socket.isInputShutdown());
    8.         System.out.println("isOutputShutdown:" + socket.isOutputShutdown());
    9.         System.out.println("isClosed:" + socket.isClosed());
    10.         System.out.println();
    11.     }
    12.     public static void main(String[] args) throws Exception
    13.     {
    14.         Socket socket = new Socket("www.ptpress.com.cn", 80);
    15.         printState(socket);
    16.         socket.shutdownInput();
    17.         printState(socket);
    18.         socket.shutdownOutput();
    19.         printState(socket);
    20.     }
    21. }
    复制代码
    在运行上面的代后,将得到如下的输出结果:
    isInputShutdown:false
    isOutputShutdown:false
    isClosed:false
    isInputShutdown:true
    isOutputShutdown:false
    isClosed:false
    isInputShutdown:true
    isOutputShutdown:true
    isClosed:false

    从输出结果可以看出,isClosed方法一直返回false,因此,可以肯定,shutdownInput和shutdownOutput并不影响Socket对象的状态。

    本 文出自 “软件改变整个宇宙” 博客,请务必保留此出处http://androidguy.blog.51cto.com/974126/214455


      
      
       
       

         
       

         
       
      
    复制代码

    源码下载:http://file.javaxxz.com/2014/11/6/000414609.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-25 10:01 , Processed in 0.374908 second(s), 50 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表