Java提供的TCP编程,分为客户端和服务器两种不同的API,下面首先介绍服务器API
服务器
在java中TCP连接的服务器方式是通过ServerSocket类实现,ServerSocket类有多个构造函数:
//创建一个没有绑定端口和地址的服务端 socket
public ServerSocket();
//创建一个服务端 socket并且执行bind()方法绑定到指定的port端口上
public ServerSocket(int port);
//创建一个服务端socket并且执行bind()方法绑定到指定的port端口上,并且设置客户端请求队列大小
//backlog参数设置客户端连接的请求数
public ServerSocket(int port, int backlog)
//创建一个服务端socket并且执行bind方法绑定到指定的bindAddr地址上
public ServerSocket(int port, int backlog, InetAddress bindAddr)
一般情况应该使用不带参数的构造方法创建ServerSocket对象,因为这种方式可以在执行bind方法之前进行参数配置。服务器端可以设置的参数配置包括如下:
//是否复用未完全关闭的地址端口,比如可以使用tcp连接处于TIME_WAIT状态或者 2MSL状态的端口
serverSocket.setReuseAddress(true);
//该值有两层含义 1.设置每一个与客户端建立连接的socket内部缓存大小
// 2.向远程主机公布自己设置的TCP协议的接受窗口大小
//该值可以在accept方法之后进行设置,但是如果设置的值大小超过了64k,
//那么必须在执行bind方法之前设置该值。因此必须采用无参构造函数
//创建ServerSocket对象,并且在绑定地址之前调用setReceiveBufferSize方法设置该值;
serverSocket.setReceiveBufferSize(1024);
//设置调用accept方法的阻塞时间,如果阻塞时间超过了设定值,那么会抛出SocketTimeoutException
//异常,如果不设置或者设置为0表示一直会阻塞
serverSocket.setSoTimeout(100);
//设置tcp连接的性能偏好策略,这三个参数分别代表不同的属性,具体值是相对于属性的权重
//三个参数分别表示: 用最少时间建立连接 延迟 带宽
serverSocket.setPerformancePreferences(1,1,1);
设置完相关参数之后,执行绑定操作,把serversocket绑定的本地的某个ip地址和端口上,并且设置可接受的客户端最大请求数
serverSocket.bind(new InetSocketAddress("localhost",8000),50);
绑定结束之后,调用accept方法,开始监听客户端发来的tcp连接请求:
Socket socket = serverSocket.accept();
当调用accept方法后,服务端进入阻塞状态,直到有客户端请求过来或者SoTimeout时间到达。当客户端有tcp连接请求过来,该方法会返回Socket对象用于处理服务器和客户段之前的网络通信。
全部代码如下:
public class TCPServer {
public static void main(String[] args) throws Exception {
try(ServerSocket serverSocket = new ServerSocket()){
serverSocket.setReuseAddress(true)
serverSocket.setReceiveBufferSize(1024);
serverSocket.setSoTimeout(0);
serverSocket.setPerformancePreferences(1,1,0);
serverSocket.bind(new InetSocketAddress("localhost",8000),50);
Socket socket = serverSocket.accept();
InputStream read = socket.getInputStream();
byte[] data = new byte[1024];
int count = read.read(data);
String clientData = new String(data,0,count,"utf-8");
System.out.println("client:"+clientData);
OutputStream write = socket.getOutputStream();
data = "I'm tcp server !".getBytes();
write.write(data);
read.close();
write.close();
socket.close();
}
}
}
客户端
下面介绍客户端API使用,客户端使用Socket类,Socket类也有多个实现方法:
public Socket();
//通过代理访问远程服务器
public Socket(Proxy proxy)
//创建并连接访问host和port的服务器主机的socket
public Socket(String host, int port)
//以InetAddress的方式创建客户端的socket
public Socket(InetAddress address, int port)
//创建访问远程主机host,并且绑定到本地IP localAddr和本地端口localPort
public Socket(String host, int port, InetAddress localAddr, int localPort)
//以InetAddress方式绑定远程主机和本地ip地址
public Socket(InetAddress address, int port, InetAddress localAddr,int localPort)
以上构造方法,除了无参构造方法,其他构造方法,在创建Socket对象的同时,会发起于服务段的TCP连接,通常会在发起服务器连接之前,进行相关参数设置,因此,通常我们使用无惨构造方法,创建Socket对象之后,再调用bind方法绑定到客户端本地的ip和本地端口。
socket.bind(new InetSocketAddress("localhost",8888));
下面进行socket的参数进行设置:
//设置阻塞时间,TCP中的阻塞时间包括: ServerSocket.accept() SocketInputStream.read()
//serverSocket设置的是accept的阻塞时间,socket设置的是read的阻塞时间
socket.setSoTimeout(100);
//是否复用未完全关闭的地址端口,比如可以使用tcp连接处于TIME_WAIT状态或者 2MSL状态的端口
socket.setReuseAddress(true);
//是否启用Nagle's算法
socket.setTcpNoDelay(true);
//如果该值设置为true,当TCP双方2个小时(取决于具体实现)内没有数据交互的时候,TCP自动发送keepalive探测
//该探测回有三种返回:1.正常返回ACK报文,表示服务正常 2.返回RST报文,表示服务已经宕机重启 3.没有返回结果
//表示服务已经宕机,socket已经关闭
socket.setKeepAlive(true);
//启用并设置延迟关闭socket连接,该值通常可以设置三种取值:
//false,any: 当设置为false的时候,socket在执行close的时候,会立即关闭。底层系统接管输出流,负责把数据发送出去
//true,2000: socket会在关闭之前等到数据发送完成。如果时间超过了2000毫秒,缓冲区数据还没发送完成,
// 那么socket会立即关闭,缓冲区数据被丢弃,然后发送RST报文
//true,0: 那么socket会立即关闭,缓冲区数据被丢弃,然后发送RST报文
socket.setSoLinger(true,1000);
//设置是否启用紧急数据 通常不启用
socket.setOOBInline(false);
//设置接受缓存大小
socket.setReceiveBufferSize(1024);
//设置发送缓存大小
socket.setSendBufferSize(1024);
//设置TCP连接的策略
socket.setPerformancePreferences(1,0,1);
设置完参数之后,就可以调用connect方法连接服务器了:
//这里的localhost是远程主机的名称,9999是远程主机的端口号
socket.connect(new InetSocketAddress("localhost",9999));
下面的是全部客户端的代码:
public class TCPClient {
public static void main(String[] args) throws Exception{
try(Socket socket = new Socket()){
socket.bind(new InetSocketAddress("localhost",8888));
socket.setSoTimeout(100);
socket.setReuseAddress(true);
socket.setTcpNoDelay(true);
socket.setKeepAlive(true);
socket.setSoLinger(true,1000);
socket.setOOBInline(false);
socket.setReceiveBufferSize(1024);
socket.setSendBufferSize(1024);
socket.setPerformancePreferences(1,0,1);
socket.connect(new InetSocketAddress("localhost",9999));
OutputStream out = socket.getOutputStream();
out.write("I'm client".getBytes());
InputStream read = socket.getInputStream();
byte[] data = new byte[1024];
int count = read.read(data);
String serverData = new String(data,0,count,"utf-8");
System.out.println("Tcp Client:"+serverData);
out.close();
read.close();
}
}
}