博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java网络编程从入门到精通(13):使用Socket类接收和发送数据
阅读量:7268 次
发布时间:2019-06-29

本文共 8080 字,大约阅读时间需要 26 分钟。

网络应用分为客户端和服务端两部分,而
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
或域名以及端口号直接传入构造函数即可。下面的代码
是一个连接服务端程序的例子程序:
package
 mysocket;
import
 java.net.
*
;
public
 
class
 MyConnection
{
    
public
 
static
 
void
 main(String[] args)
    {
        
try
        {
            
if
 (args.length 
>
 
0
)
            {
                Socket socket 
=
 
new
 Socket(args[
0
], 
80
);
                System.out.println(args[
0
+
 
"
已连接成功!
"
);
            }
            
else
                System.out.println(
"
请指定IP或域名!
"
);
        }
        
catch
 (Exception e)
        {
            System.err.println(
"
错误信息:
"
 
+
 e.getMessage());
        }
    }
}
在上面的
中,通过命令行参数将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
类连接服务器可以判断一台主机有哪些端口被打开。下面的代码
是一个扫描本机有哪些端口被打开的程序。
package
 mysocket;
import
 java.net.
*
;
public
 
class
 MyConnection1 
extends
 Thread
{
    
private
 
int
 minPort, maxPort;
    
public
 
MyConnection1
(
int
 minPort, 
int
 maxPort)
    {
        
this
.minPort 
=
 minPort;
        
this
.maxPort 
=
 maxPort;
    }
    
public
 
void
 run()
    {
        
for
 (
int
 i 
=
 minPort; i 
<=
 maxPort; i
++
)
        {
            
try
            {
                Socket socket 
=
 
new
 Socket(
"
127.0.0.1
"
, i);
                System.out.println(String.valueOf(i) 
+
 
"
:ok
"
);
                socket.close();
            }
            
catch
 (Exception e)
            {
            }
        }
    }
    
public
 
static
 
void
 main(String[] args)
    {
        
int
 minPort 
=
 Integer.parseInt(args[
0
]), maxPort 
=
 Integer
                .parseInt(args[
1
]);
        
int
 threadCount 
=
 Integer.parseInt(args[
2
]);
        
int
 portIncrement 
=
 ((maxPort 
-
 minPort 
+
 
1
/
 threadCount)
                
+
 (((maxPort 
-
 minPort 
+
 
1
%
 threadCount) 
==
 
0
 
?
 
0
 : 
1
);
        
MyConnection
1[] instances 
=
 
new
 
MyConnection
1[threadCount];
        
for
 (
int
 i 
=
 
0
; i 
<
 threadCount; i
++
)
        {
            instances[i] 
=
 
new
 
MyConnection1
(minPort 
+
 portIncrement 
*
 i, minPort
                    
+
 portIncrement 
-
 
1
 
+
 portIncrement 
*
 i);
            instances[i].start();
        }
    }
}
上面代码
通过一个指定的端口范围(如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
返回的信息。
package
 mysocket;
import
 java.net.
*
;
import
 java.io.
*
;
public
 
class
 MyConnection2
{
    
public
 
static
 
void
 main(String[] args) 
throws
 Exception
    {
        Socket socket 
=
 
new
 Socket(
"
www.ptpress.com.cn
"
80
);
        
//
 向服务端程序发送数据
        OutputStream ops  
=
 socket.getOutputStream();        
        OutputStreamWriter opsw 
=
 
new
 OutputStreamWriter(ops);
        BufferedWriter bw 
=
 
new
 BufferedWriter(opsw);
        
        bw.write(
"
hello world\r\n\r\n
"
);
        bw.flush();
        
        
//
 从服务端程序接收数据
        InputStream ips 
=
 socket.getInputStream();
        InputStreamReader ipsr 
=
 
new
 InputStreamReader(ips);
        BufferedReader br 
=
 
new
 BufferedReader(ipsr);
        String s 
=
 
""
;        
        
while
((s 
=
 br.readLine()) 
!=
 
null
)
            System.out.println(s);        
        socket.close();
    }
}
在编写上面代码时要注意如下两点:
1. 
为了提高数据传输的效率,Socket
类并没有在每次调用write
方法后都进行数据传输,而是将这些要传输的数据写到一个缓冲区里(默认是8192
个字节),然后通过flush
方法将这个缓冲区里的数据一起发送出去,因此,bw.flush();
是必须的。
2. 
在发送字符串时之所以在Hello World
后加上 “\r\n\r\n”
,这是因为HTTP
协议头是以“\r\n\r\n”
作为结束标志(HTTP
协议的详细内容将在以后讲解),因此,通过在发送字符串后加入“\r\n\r\n”
,可以使服务端程序认为HTTP
头已经结束,可以处理了。如果不加“\r\n\r\n”
,那么服务端程序将一直等待HTTP
头的结束,也就是“\r\n\r\n”
。如果是这样,服务端程序就不会向客户端发送响应信息,而
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
对象的各种状态的产生过程。
package
 mysocket;
import
 java.net.
*
;
public
 
class
 MyCloseConnection
{
    
public
 
static
 
void
 printState(Socket socket, String name)
    {
        System.out.println(name 
+
 
"
.isClosed():
"
 
+
 socket.isClosed());
        System.out.println(name 
+
 
"
.isConnected():
"
 
+
 socket.isConnected());
        
if
 (socket.isClosed() 
==
 
false
 
&&
 socket.isConnected() 
==
 
true
)
            System.out.println(name 
+
 
"
处于连接状态!
"
);
        
else
            System.out.println(name 
+
 
"
处于非连接状态!
"
);
        System.out.println();
    }
    
public
 
static
 
void
 main(String[] args) 
throws
 Exception
    {
        Socket socket1 
=
 
null
, socket2 
=
 
null
;
        socket1 
=
 
new
 Socket(
"
www.ptpress.com.cn
"
80
);
        printState(socket1, 
"
socket1
"
);
        socket1.getOutputStream().close();
        printState(socket1, 
"
socket1
"
);
        socket2 
=
 
new
 Socket();
        printState(socket2, 
"
socket2
"
);
        socket2.close();
        printState(socket2, 
"
socket2
"
);
    }
}
运行上面的代码后,将有如下的输出结果:
    
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()
。下面的代码
演示了只关闭输入、输出流的过程:
package
 mysocket;
import
 java.net.
*
;
public
 
class
 MyCloseConnection1
{
    
public
 
static
 
void
 printState(Socket socket)
    {
        System.out.println(
"
isInputShutdown:
"
 
+
 socket.isInputShutdown());
        System.out.println(
"
isOutputShutdown:
"
 
+
 socket.isOutputShutdown());
        System.out.println(
"
isClosed:
"
 
+
 socket.isClosed());
        System.out.println();
    }
    
public
 
static
 
void
 main(String[] args) 
throws
 Exception
    {
        Socket socket 
=
 
new
 Socket(
"
www.ptpress.com.cn
"
80
);
        printState(socket);
        socket.shutdownInput();
        printState(socket);
        socket.shutdownOutput();
        printState(socket);
    }
}
在运行上面的代
将得到如下的输出结果
    isInputShutdown:false
isOutputShutdown:false
isClosed:false
isInputShutdown:true
isOutputShutdown:false
isClosed:false
isInputShutdown:true
isOutputShutdown:true
isClosed:false
从输出结果可以看出,isClosed
方法一直返回false
,因此,可以肯定,shutdownInput
shutdownOutput
并不影响Socket
对象的状态。
本文转自 androidguy 51CTO博客,原文链接:http://blog.51cto.com/androidguy/214455,如需转载请自行联系原作者
你可能感兴趣的文章
php页面输出时,js设置input框的选中值
查看>>
Linux 系统下 matplotlib 中文乱码解决办法
查看>>
Public Prize
查看>>
QuickSort
查看>>
asp.net的sessionState节点详解
查看>>
RedHat6.2搭建FTP服务器
查看>>
Following Orders(拓扑排序)
查看>>
[POJ 3087] Shuffle'm Up
查看>>
TensorFlow深度学习入门笔记(一)写在前面——如何入门深度学习
查看>>
各种手机的SD卡
查看>>
8086CPU访问地址为123C8H的内存单元
查看>>
20145237 《信息安全系统设计基础》第三周学习总结
查看>>
多线程-定时器Timer
查看>>
host
查看>>
Three.js
查看>>
关于EF的一个简单Demo
查看>>
hive-hbase性能问题
查看>>
kafka集群的搭建
查看>>
藏在正则表达式里的陷阱-----转发记录问题
查看>>
GridView控件中插入自定义删除按钮并弹出确认框
查看>>