Java编程那些事儿系列文章(全集目录)
13.2.3 TCP编程
按照前面的介绍,网络通讯的方式有TCP和UDP两种,其中TCP方式的网络通讯是指在通讯的过程中保持连接,有点类似于打电话,只需要拨打一次号码(建立一次网络连接),就可以多次通话(多次传输数据)。这样方式在实际的网络编程中,由于传输可靠,类似于打电话,如果甲给乙打电话,乙说没有听清楚让甲重复一遍,直到乙听清楚为止,实际的网络传输也是这样,如果发送的一方发送的数据接收方觉得有问题,则网络底层会自动要求发送方重发,直到接收方收到为止。
在Java语言中,对于TCP方式的网络编程提供了良好的支持,在实际实现时,以java.net.Socket类代表客户端连接,以java.net.ServerSocket类代表服务器端连接。在进行网络编程时,底层网络通讯的细节已经实现了比较高的封装,所以在程序员实际编程时,只需要指定IP地址和端口号码就可以建立连接了。正是由于这种高度的封装,一方面简化了Java语言网络编程的难度,另外也使得使用Java语言进行网络编程时无法深入到网络的底层,所以使用Java语言进行网络底层系统编程很困难,具体点说,Java语言无法实现底层的网络嗅探以及获得IP包结构等信息。但是由于Java语言的网络编程比较简单,所以还是获得了广泛的使用。
在使用TCP方式进行网络编程时,需要按照前面介绍的网络编程的步骤进行,下面分别介绍一下在Java语言中客户端和服务器端的实现步骤。
在客户端网络编程中,首先需要建立连接,在Java API中以java.net.Socket类的对象代表网络连接,所以建立客户端网络连接,也就是创建Socket类型的对象,该对象代表网络连接,示例如下:
Socket socket1 = new Socket(“192.168.1.103”,10000);
Socket socket2 = new Socket(“www.sohu.com”,80);
上面的代码中,socket1实现的是连接到IP地址是192.168.1.103的计算机的10000号端口,而socket2实现的是连接到域名是www.sohu.com的计算机的80号端口,至于底层网络如何实现建立连接,对于程序员来说是完全透明的。如果建立连接时,本机网络不通,或服务器端程序未开启,则会抛出异常。
连接一旦建立,则完成了客户端编程的第一步,紧接着的步骤就是按照“请求-响应”模型进行网络数据交换,在Java语言中,数据传输功能由Java IO实现,也就是说只需要从连接中获得输入流和输出流即可,然后将需要发送的数据写入连接对象的输出流中,在发送完成以后从输入流中读取数据即可。示例代码如下:
OutputStream os = socket1.getOutputStream(); //获得输出流
InputStream is = socket1.getInputStream(); //获得输入流
上面的代码中,分别从socket1这个连接对象获得了输出流和输入流对象,在整个网络编程中,后续的数据交换就变成了IO操作,也就是遵循“请求-响应”模型的规定,先向输出流中写入数据,这些数据会被系统发送出去,然后在从输入流中读取服务器端的反馈信息,这样就完成了一次数据交换过程,当然这个数据交换过程可以多次进行。
这里获得的只是最基本的输出流和输入流对象,还可以根据前面学习到的IO知识,使用流的嵌套将这些获得到的基本流对象转换成需要的装饰流对象,从而方便数据的操作。
最后当数据交换完成以后,关闭网络连接,释放网络连接占用的系统端口和内存等资源,完成网络操作,示例代码如下:
socket1.close();
这就是最基本的网络编程功能介绍。下面是一个简单的网络客户端程序示例,该程序的作用是向服务器端发送一个字符串“Hello”,并将服务器端的反馈显示到控制台,数据交换只进行一次,当数据交换进行完成以后关闭网络连接,程序结束。实现的代码如下:
package tcp;
import java.io.*;
import java.net.*;
/**
* 简单的Socket客户端
* 功能为:发送字符串“Hello”到服务器端,并打印出服务器端的反馈
*/
public class SimpleSocketClient {
public static void main(String[] args) {
Socket socket = null;
InputStream is = null;
OutputStream os = null;
//服务器端IP地址
String serverIP = "127.0.0.1";
//服务器端端口号
int port = 10000;
//发送内容
String data = "Hello";
try {
//建立连接
socket = new Socket(serverIP,port);
//发送数据
os = socket.getOutputStream();
os.write(data.getBytes());
//接收数据
is = socket.getInputStream();
byte[] b = new byte[1024];
int n = is.read(b);
//输出反馈数据
System.out.println("服务器反馈:" + new String(b,0,n));
} catch (Exception e) {
e.printStackTrace(); //打印异常信息
}finally{
try {
//关闭流和连接
is.close();
os.close();
socket.close();
} catch (Exception e2) {}
}
}
}
在该示例代码中建立了一个连接到IP地址为127.0.0.1,端口号码为10000的TCP类型的网络连接,然后获得连接的输出流对象,将需要发送的字符串“Hello”转换为byte数组写入到输出流中,由系统自动完成将输出流中的数据发送出去,如果需要强制发送,可以调用输出流对象中的flush方法实现。在数据发送出去以后,从连接对象的输入流中读取服务器端的反馈信息,读取时可以使用IO中的各种读取方法进行读取,这里使用最简单的方法进行读取,从输入流中读取到的内容就是服务器端的反馈,并将读取到的内容在客户端的控制台进行输出,最后依次关闭打开的流对象和网络连接对象。