1. TCP通信
1.1. Socket原理
1.1.1. Socket简介
socket通常称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。
应用程序通常通过“套接字”向网络发出请求或者应答网络请求。Socket和ServerSocket类库位于java .net包中。ServerSocket用于服务端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。
1.1.2. 获取本地地址和端口号
java.net.Socket为套接字类,其提供了很多方法,其中我们可以通过Socket获取本地的地址以及端口号。
intgetLocalPort()
该方法用于获取本地使用的端口号
InetAddressgetLocalAddress()
该方法用于获取套接字绑定的本地地址
使用InetAddress获取本地的地址方法:
StringgetCanonicalHostName()
获取此 IP 地址的完全限定域名。
StringgetHostAddress()
返回 IP 地址字符串(以文本表现形式)。
代码如下:
public void testSocket()throws Exception {
Socket socket = new Socket("localhost",8088);
InetAddress add = socket.getLocalAddress();//获取本地地址信息
System.out.println(add.getCanonicalHostName());
System.out.println(add.getHostAddress());
System.out.println(socket.getLocalPort());
}
1.1.3. 获取远端地址和端口号
Socket也提供了获取远端的地址以及端口号的方法:
intgetPort()
该方法用于获取远端使用的端口号 。
InetAddress.getInetAddress()
该方法用于获取套接字绑定的远端地址 。
代码如下:
public void testSocket()throws Exception {
Socket socket = new Socket("localhost",8088);
InetAddress inetAdd = socket.getInetAddress();
System.out.println(inetAdd.getCanonicalHostName());
System.out.println(inetAdd.getHostAddress());
System.out.println(socket.getPort());
}
1.1.4. 获取网络输入流和网络输出流
通过Socket获取输入流与输出流,这两个方法是使用Socket通讯的关键方法。封装了TCP协议的Socket是基于流进行通讯的,所以我们在创建了双方连接后,只需要获取相应的输入与输出流即可实现通讯。
InputStreamgetInputStream()
该方法用于返回此套接字的输入流。
OutputStream.getOutputStream()
该方法用于返回此套接字的输出流。
代码如下:
public void testSocket()throws Exception {
Socket socket = new Socket("localhost",8088);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
}
1.1.5. close方法
当使用Socket进行通讯完毕后,要关闭Socket以释放系统资源。
void close()
当关闭了该套接字后也会同时关闭由此获取的输入流与输出流。
1.2. Socket通讯模型
1.2.1. Server端ServerSocket监听
java.net.ServerSocket是运行于服务端应用程序中。通常创建ServerSocket需要指定服务端口号,之后监听Socket的连接。监听方法为:
Socketaccept()
该方法是一个阻塞方法,直到一个客户端通过Socket连接后,accept会封装一个Socket,该Socket封装与表示该客户端的有关的信息。通过这个Socket与该客户端进行通信。
代码如下:
…
//创建ServerSocket并申请服务端口8088
ServerSocket server = new ServerSocket(8088);
/*方法会产生阻塞,直到某个Socket连接,并返回请求连接的Socket*/
Socket socket = server.accept();
…
1.2.2. Client端Socket连接
通过上一节我们已经知道,当服务端ServerSocket调用accept方法阻塞等待客户端连接后,我们可以通过在客户端应用程序中创建Socket来向服务端发起连接。
需要注意的是,创建Socket的同时就发起连接,若连接异常会抛出异常。我们通常创建Socket时会传入服务端的地址以及端口号。
代码如下:
//参数1:服务端的IP地址,参数2:服务端的服务端口
Socket socket = newSocket(“localhost”,8088);
…
1.2.3. C-S端通信模型
C-S的全称为(Client-Server):客户端-服务器端
客户端与服务端通信模型如下:
图- 1
1. 服务端创建ServerSocket
2. 通过调用ServerSocket的accept方法监听客户端的连接
3. 客户端创建Socket并指定服务端的地址以及端口来建立与服务端的连接
4. 当服务端accept发现客户端连接后,获取对应该客户端的Socket
5. 双方通过Socket分别获取对应的输入与输出流进行数据通讯
6. 通讯结束后关闭连接。
代码如下:
/**
* Server端应用程序
*/
public class Server {
public static void main(String[] args) {
ServerSocket server = null;
try {
//创建ServerSocket并申请服务端口为8088
server = new ServerSocket(8088);
//侦听客户端的连接
Socket socket = server.accept();
//客户端连接后,通过该Socket与客户端交互
//获取输入流,用于读取客户端发送过来的消息
InputStream in = socket.getInputStream();
BufferedReader reader
= new BufferedReader(
new InputStreamReader(
in,"UTF-8"
)
);
//获取输出流,用于向该客户端发送消息
OutputStream out = socket.getOutputStream();
PrintWriter writer
= new PrintWriter(
new OutputStreamWriter(
out,"UTF-8"
),true
);
//读取客户端发送的消息
String message = reader.readLine();
System.out.println("客户端说:"+message);
//向客户端发送消息
writer.println("你好客户端!");
} catch (Exception e) {
e.printStackTrace();
} finally{
if(server != null){
try {
server.close();
} catch (IOException e) {
}
}
}
}
}
/**
* Client端应用程序
*/
public class Client {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("localhost",8088);
//获取输入流,用于读取来自服务端的消息
InputStream in = socket.getInputStream();
BufferedReader reader
= new BufferedReader(
new InputStreamReader(
in,"UTF-8"
)
);
//获取输出流,用于向服务端发送消息
OutputStream out
= socket.getOutputStream();
OutputStreamWriter osw
= new OutputStreamWriter(out,"UTF-8");
PrintWriter writer
= new PrintWriter(osw,true);
//向服务端发送一个字符串
writer.println("你好服务器!");
//读取来自客户端发送的消息
String message = reader.readLine();
System.out.println("服务器说:"+message);
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if(socket != null){
//关闭Socket
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}