1、TCP/IP是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路;
一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路来进行通信;
2、Socket编程主要是指基于TCP/IP协议的网络编程。
Java对基于TCP/IP协议的网络通信提供了良好的封装;
Java使用Socket对象来代表两端(服务器程序和客户端程序)的通信端口;并通过Socket产生的IO流来进行通信。
其中 ServerSocket 类表示 Socket 服务器端,Socket 类表示 Socket 客户端。这两个类中的方法都比较相同。
简单来说,Java的网络编程是使用Socket对象进行的I/O编程。
1、ServerSocket 类
用来实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
服务器套接字一次可以与一个套接字连接,如果多台客户端同时提出连接请求,请求连接的客户端会被存入一个队列中,然后从中取出一个套接字与服务器新建的套接字连接起来。
若请求连接大于最大容纳数,则多出的连接请求被拒绝;默认的队列大小是 50。
1. ServerSocket 的构造方法
ServerSocket() 创建非绑定服务器套接字。 |
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。backlog (默认队列大小是50) |
ServerSocket(int port, int backlog) 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
ServerSocket(int port, int backlog, InetAddress bindAddr) 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
2. ServerSocket 的常用方法
Socket |
accept() 侦听并接受到此套接字的连接。 |
void |
bind(SocketAddress endpoint) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 |
void |
bind(SocketAddress endpoint, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 |
void |
close() 关闭此套接字。 |
ServerSocketChannel |
getChannel() 返回与此套接字关联的唯一 ServerSocketChannel 对象(如果有)。 |
InetAddress |
getInetAddress() 返回此服务器套接字的本地地址。 |
int |
getLocalPort() 返回此套接字在其上侦听的端口。 |
SocketAddress |
getLocalSocketAddress() 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null 。 |
int |
getReceiveBufferSize() 获取此 ServerSocket 的 SO_RCVBUF 选项的值,该值是将用于从此 ServerSocket 接受的套接字的建议缓冲区大小。 |
调用 accept() 方法会返回一个和客户端 Socket 对象相连接的 Socket 对象。
2、Socket 类
用来实现客户端套接字。用于呼叫远端机器上的一个端口,主动向服务器端发送数据(当连接建立后也能接收数据)。
1. Socket 的构造方法
|
Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字 |
|
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
|
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
|
Socket(Proxy proxy) 创建一个未连接的套接字并指定代理类型(如果有),该代理不管其他设置如何都应被使用。 |
|
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。 |
|
Socket(String host, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程主机上的指定远程端口。 |
2. Socket 的常用方法
方法摘要 | |
---|---|
void |
bind(SocketAddress bindpoint) 将套接字绑定到本地地址。 |
void |
close() 关闭此套接字。 |
void |
connect(SocketAddress endpoint) 将此套接字连接到服务器。 |
void |
connect(SocketAddress endpoint, int timeout) 将此套接字连接到服务器,并指定一个超时值。 |
boolean |
isBound() 返回套接字的绑定状态。 |
boolean |
isClosed() 返回套接字的关闭状态。 |
boolean |
isConnected() 返回套接字的连接状态。 |
boolean |
isInputShutdown() 返回是否关闭套接字连接的半读状态 (read-half)。 |
boolean |
isOutputShutdown() 返回是否关闭套接字连接的半写状态 (write-half)。 |
void |
setSoLinger(boolean on, int linger) 启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。 |
void |
setSoTimeout(int timeout) 启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。 |
SO_TIMEOUT选项
该选项表示accept()方法等待客户端连接的时间,以毫秒为单位。
当accept()方法在超时时间内没有获得连接,就会抛出SocketTImeoutException。
选项必须在进入阻塞操作前被启用才能生效。超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。
SO_REUSEADDR选项
关闭 TCP 连接时,该连接可能在关闭后的一段时间内保持超时状态。
此时,是否允许新的ServerSocket绑定到与旧的ServerSocket 同样的端口上,在某些操作系统上允许重用端口,有些则不允许。很多服务器程序都是用固定的端口,当程序关闭后,端口可能还被占用一段时间,如果此时立刻重启服务器,服务器就会无法绑定端口,抛出BindException异常。为了确保不发生这种异常,就可以调用ServerSocket的setReuseAddress(boolean on)方法:
if(!serverSocket.getResuseAddress())
serverSocket.setReuseAddress(boolean on);
该方法必须在绑定端口前设置。
SO_RCVBUF选项
用于设置内部套接字接收缓冲区的大小和设置公布到远程同位体的 TCP 接收窗口的大小。
该方法必须在绑定端口前设置。
服务器端流程:
客户端流程:
/**
* 服务器端
*/
public class Server {
public static void main(String[] args) throws IOException {
// 创建一个ServerSocket,用于监听客户端的连接请求
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("127.0.0.1", 8000));
// 使用循环不断地接受来自客户端的连接
while (true) {
Socket socket= serverSocket.accept();
// IO流交互通信
PrintStream printStream = new PrintStream(socket.getOutputStream(), true, "UTF-8");
printStream.println("服务器说:" + socket.getInetAddress() + ",来了老弟");
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
System.out.println("来自客户端的信息:" + in.readLine());
// 关闭
printStream.close();
in.close();
socket.close();
}
}
}
/**
* 客户端的代码
*/
public class Client {
public static void main(String[] args) throws IOException {
// 创建一个Socket,向服务器发出连接请求
Socket socket = new Socket();
socket.connect(new InetSocketAddress("127.0.0.1", 8000));
// IO流交互通信
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
System.out.println("来自服务器的信息:" + reader.readLine());
PrintStream ps = new PrintStream(socket.getOutputStream(), true, "UTF-8");
ps.println("客户端向你问好");
// 关闭
reader.close();
socket.close();
}
}
—— Stay Hungry. Stay Foolish. 求知若饥,虚心若愚。