java使⽤Socket类接收和发送数据
⽹络应⽤分为客户端和服务端两部分,⽽Socket类是负责处理客户端通信的Java类。通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据。在本⽂及后⾯的数篇⽂章中将详细讨论Socket类的使⽤,内容包括Socket类基础、各式各样的连接⽅式、get和set⽅法、连接过程中的超时以及关闭⽹络连接等。
在本⽂中,我们将讨论使⽤Socket类的基本步骤和⽅法。⼀般⽹络客户端程序在连接服务程序时要进⾏以下三步操作。
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.*;
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)
{
}
}
}
在上⾯的中,通过命令⾏参数将IP或域名传⼊程序,然后通过Socket socket = new Socket(args[0], 80)连接通过命令⾏参数所指定的IP或域名的80端⼝。由于Socket类的构造函数在定义时使⽤了throws,因此,在调⽤Socket类的构造函数时,必须使⽤try…catch语句来捕捉错误,或者对main函数使⽤throws语句来抛出错误。
使⽤Socket类连接服务器可以判断⼀台主机有哪些端⼝被打开。下⾯的代码是⼀个扫描本机有哪些端⼝被打开的程序。
⼆、发送和接收数据
在Socket类中最重要的两个⽅法就是getInputStream和getOutputStream。这两个⽅法分别⽤来得到⽤于读取和写⼊数据的InputStream和OutputStream对象。在这⾥的InputStream读取的是服务器程序向客户端发送过来的数据,⽽OutputStream是客户端要向服务端程序发送的数据。
在编写实际的⽹络客户端程序时,是使⽤getInputStream,还是使⽤getOutputStream,以及先使⽤谁后使⽤谁由具体的应⽤决定。如通过连接邮电出版社⽹站(www.ptpress)的80端⼝(⼀般为HTTP协议所使⽤的默认端⼝),并且发送⼀个字符串,最后再读取从www.ptpress返回的信息。
package mysocket;
import java.*;
import java.io.*;
public class MyConnection2
{
public static void main(String[] args) throws Exception
{
Socket socket = new Socket("www.ptpress", 80);
// 向服务端程序发送数据
OutputStream ops = OutputStream();
OutputStreamWriter opsw = new OutputStreamWriter(ops);
BufferedWriter bw = new BufferedWriter(opsw);
bw.write("hello world\r\n\r\n");
bw.flush();
// 从服务端程序接收数据
InputStream ips = InputStream();
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对象是否曾经连接成功过,只要处于关闭状态,isClosde就返回true。如果只是建⽴⼀个未连接的Socket对象,isClose也同样返回true。如下⾯的代码将输出false。
Socket socket = new Socket();
System.out.println(socket.isClosed());
除了isClose⽅法,Socket类还有⼀个isConnected⽅法来判断Socket对象是否连接成功。看到这个名字,也许读者会产⽣误解。其实isConnected⽅法所判断的并不是Socket对象的当前连接状态,⽽是Soc
ket对象是否曾经连接成功过,如果成功连接过,即使现在isClose返回true,isConnected仍然返回true。因此,要判断当前的Socket对象是否处于连接状态,必须同时使⽤isClose和isConnected⽅法,即只有当isClose返回false,isConnected返回true的时候Socket对象才处于连接状态。下⾯的代码演⽰了上述Socket对象的各种状态的产⽣过程。
package mysocket;
import java.*;
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", 80);
printState(socket1, "socket1");
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.*;
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", 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对象的状态。
希望本⽂所述对你有所帮助,java使⽤Socket类接收和发送数据内容就给⼤家介绍到这⾥了。希望⼤家继续关注我们的⽹站!想要学习java可以继续关注本站。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论