一、基于TCP协议的网络编程
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 的构造方法
|
|
|
|
2. ServerSocket 的常用方法
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
调用 accept() 方法会返回一个和客户端 Socket 对象相连接的 Socket 对象。
- 服务器端的 Socket使用 getOutputStream() 方法获得的输出流,将指向客户端 Socket 使用 getInputStream() 方法获得那个输入流。
- 同样,服务器端的 Socket使用的 getInputStream() 方法获得的输入流,将指向客户端 Socket 使用的 getOutputStream() 方法获得的那个输出流。
- 也就是说,当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之同样如此。
2、Socket 类
用来实现客户端套接字。用于呼叫远端机器上的一个端口,主动向服务器端发送数据(当连接建立后也能接收数据)。
1. Socket 的构造方法
| |
| |
| |
| |
| |
|
2. Socket 的常用方法
方法摘要 | |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 接收窗口的大小。
该方法必须在绑定端口前设置。
基于TCP的套接字编程实现流程
服务器端流程:
- 创建服务器套接字(ServerSocket)
- 将套接字绑定到一个本地地址和端口上(bind)
- 将套接字设定为监听模式,准备接受客户端请求(listen)
- 阻塞等待客户端请求到来。当请求到来后,接受连接请求,返回一个新的对应于此客户端连接的套接字socketClient(accept)
- 用返回的套接字socketClient和客户端进行通信(IO流操作)
- 返回,等待另一个客户端请求(accept)
- 关闭套接字(close)
客户端流程:
- 创建客户端套接字(Socket)
- 向服务器发出连接请求(connect)
- 和服务器进行通信(IO流操作)
- 关闭套接字(close)
/**
* 服务器端
*/
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. 求知若饥,虚心若愚。