Java网络编程_基于TCP协议的网络编程(一)

时间:2022-04-06 20:18:23

TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟网络链路,两端的程序就可以通过虚拟链路进行通信。Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流进行网络通信。

使用ServerSocket创建TCP服务器端

在两个通信实体之间并没有服务器端和客户端之分,但在两个通信实体没有建立虚拟链路之前,必须有一个通信实体先做出“主动姿态”,主动接收来自其他通信实体的连接请求。

Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。ServerSocket包含一个监听来自客户端请求的方法。

方法
Socket accept() 如果接收到一个客户端Socket的连接请求,该方法将返回一个与客户端Socket对应的Socket(每个TCP连接有两个Socket)否则该方法将一直处于等待状态,线程也被阻塞
ServerSocket所提供的构造器
ServerSocket(int port) 用指定的端口port来创建一个ServerSocket。该端口应该有一个有效的端口数值,即0~65535
ServerSoccket(int port,int backlog) 增加一个用来改变连接队列长度的参数backlog
ServerSocket(int port,int backlog,InetAddress localAddr) 在机器存在多个IP地址的情况下,允许通过localAddr参数来指定将ServerSocket绑定到指定的IP地址。

使用Socket进行通信

客户端通常可以使用Socket的构造器来连接到指定服务器,Socket通常可以使用如下两个构造器。

构造器
Socket(InetAddress/String remoteAddress,int port) 创建连接到指定远程主机、远程端口的Socket,该构造器没有指定本地地址、本地端口,默认使用本地主机的默认IP地址,默认使用系统动态分配的端口
Socket(InetAddress/String remoteAddress,int port,InetAddress localAddr,int localPort) 创建连接到指定远程主机、远程端口的Socket,并指定本地IP地址和本地端口,适用于本地主机有多个IP地址的情形

当客户端、服务器端产生了对应的Socket之后,程序无须再区分服务器端、客户端,而是通过各自的Socket进行通信。Socket提供了两个方法来获取输入流和输出流。

方法
InputStream getInputStream() 返回该Socket对象对应的输入流,让程序通过该输入流从Socket中取出数据
OutputStream getOutputStream() 返回该Socket对象对应的输出流,让程序输出流向Socket中输出数据

代码示例,一个简单的通讯
服务端:

public class Server {

public static void main(String[] args) throws IOException{

//创建一个ServerSocket,用于监听客户端Socket的连接请求
ServerSocket ss = new ServerSocket(30000);
//采用循环换不断地接收来自客户端的请求
while (true){
//每当接收到客户端Socket的请求时,服务器端也对应产生一个Socket
Socket s = ss.accept();
//将Socket对应的输出流包装成PrintStream
PrintStream ps = new PrintStream(s.getOutputStream());
//进行普通的IO操作
ps.println("您好,您收到了服务器的问候!");
//关闭输出流,关闭Socket
ps.close();
s.close();
}
}
}

客户端:

public class Client {

public static void main(String[] args) throws IOException{
//其中“1227.0.0.1”代表本机的IP地址,因为服务端和客户端都是在本机运行
Socket socket = new Socket("127.0.0.1", 30000);
//将Socket对应的输入流包装成BufferedReader
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
//进行普通IO操作
String line = br.readLine();
System.out.println("来自服务器的数据:" + line);
//关闭输入流,关闭Socket
br.close();
socket.close();
}
}

在实际应用中,程序可能不想让执行网络连接、读取服务器数据的进程一直阻塞,而是希望当网络连接、读取操作超过合理时间之后,系统自动认为操作失败,这个合理时间就是超时时长。(在指定时间内通信未到达,断开连接)

//Socket对象提供了一个setSoTimeout(int timeout)方法来设置超时时长
Socket s = new Socket("127.0.0.1", 30000);
//设置10秒之后即认为超时
s.setSoTimeouut(10000);

当Socket连接服务器超过指定时长,进行处理。

//创建一个无连接的Socket
Socket s = new Socket();
//让该Socket连接到远程服务器,如果经过10秒还未连接上,则认为连接超时
s.connect(new InetSocketAddress (host,port),10000);

两种超时的区别,第一种是读写超时,在已经连通,但在通信过程中,某条信息的传送超过指定时间,报错。第二种是链接超时,还未连通,连接时间超过指定时间,报错。