目录
一、Socket概念
Socket使用TCP,提供两台计算机之间的通信,主要用于描述IP和端口,在建立网络连接时使用。
二、Socket基本操作
- 连接远程机器
- 发送、接受数据
- 关闭连接
- 绑定端口
- 监听入站信息
- 在绑定端口上接受来自远程机器的连接
其中Socket类(客户端和服务器均可使用)提供了前3个操作方法;而SeverSocket类(仅服务器使用)提供后3个操作方法,等待客户端的连接。
当本地主机与远程主机建立连接后,两端就会从创建的Socket中得到输入流和输出流,然后使用这两个流相互发送数据。
三、Socket建立通信原理
Socket所在位置介于应用层和传输层之间,如下图:
首先服务端初始化ServerSocket,然后对指定的端口进行绑定,对端口进行监听,通过调用accept() 阻塞,接着循环等待客户端发起连接;当连接成功时,两端都会创建一个 Socket 对象,然后将两端的输入流和输出流相互交叉连接,接下来客户端和服务器端就可以通信了:
具体步骤:
- 服务器端实例化一个 java.net.ServerSocket 对象
- 服务器端调用 ServerSocket.accept() 方法,从此阻塞等待直到有客户端发起连接(服务器等待中....)
- 客户端1实例化一个Socket对象,指定服务器名称和端口号请求连接
- 如果通信建立,则在客户端创建一个Socket对象能够与服务器进行通信
- 此时服务器端的accept()方法返回一个新的socket引用,与客户端socket连接
接下来就可以使用IO流进行通信了。两端输入流和输出流的连接关系为:
- 客户端输出流 --> 服务器端输入流
- 客户端输入流 --> 服务器端输出流
四、服务器端:ServerSocket类
1、构造方法
java.net.Server类有四个构造方法用于创建服务器套接字,参数个数为0~3:
1 | public ServerSocket (int port) throws IOException 创建绑定到特定端口的服务器套接字 |
2 | public ServerSocket (int port, int backlog) throws IOException 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号 |
3 | public ServerSocket (int port, int backlog, InetAddress address) throws IOException 使用指定的端口、监听backlog、要绑定到的本地 IP 地址创建服务器 |
4 | public ServerSocket() throws IOException 创建非绑定服务器套接字(如果此方法使用时没有抛出异常,就意味着程序已经成功绑定到指定的端口,并且监听客户端请求。感觉可以用来检测是否绑定成功) |
2、常用方法
1 | public int getLocalPort() 返回此套接字监听的端口 |
2 | public Socket accept() throws IOException 监听并接受此套接字的连接 |
3 | public void setSoTimeout (int timeout) 通过指定超时值启用/禁用 SO_TIMEOUT(以ms为单位) |
4 | public void bind(SocketAddress host, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号) |
五、客户端与服务器端相互沟通:Socket类
Socket类与ServerSocket类不同,它可以在两端使用以确保两端连接与通信。客户端获取一个Socket实例化对象,而服务器端通过accept方法获取一个Socket对象返回值。
1、构造方法
Socket构造方法返回并不是简单地实例化对象,而是尝试连接到指定的服务器和端口:
1 | public Socket (String host, int port) throws UnknownHostException, IOException 创建一个流套接字并将其连接到指定主机上的指定端口号 |
2 | public Socket (InetAddress host, int port) throws IOException 创建一个流套接字并将其连接到指定 IP 地址的指定端口号 |
3 | public Socket (String host, int port, InetAddress localAddress, int localPort) throws IOException 创建一个套接字并将其连接到指定远程主机上的指定远程端口 |
4 | public Socket (InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException 创建一个套接字并将其连接到指定远程地址上的指定远程端口 |
5 | public Socket () 通过系统默认类型的 SocketImpl 创建未连接套接字 |
2、常用方法
以下为一些客户端和服务器端共享的方法:
1 | public void connect(SocketAddress host, int timeout) throws IOException 将此套接字连接到服务器,并指定一个超时值 |
2 | public InetAddress getInetAddress() 返回套接字连接的地址 |
3 | public int getPort() 返回此套接字连接的远程端口 |
4 | public int getLocalPort() 返回此套接字绑定的本地端口 |
5 | public SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点地址(未连接返回 null) |
6 | public InputStream getInputStream() throws IOException 返回此套接字的输入流 |
7 | public OutputStream getOutputStream() throws IOException 返回此套接字的输出流 |
8 | public void close() throws IOException 关闭此套接字 |
六、IP地址:InetAddress类
互联网协议地址,也称作IP地址, 是网际协议中用于标识发送或接收数据报的设备的一串数字。在Socket编程中用得较多的方法如下:
1 | static InetAddress getByAddress(byte[] addr) 在给定原始 IP 地址的情况下返回 InetAddress 对象 |
2 | static InetAddress getByAddress(String host, byte[] addr) 根据提供的主机名和 IP 地址创建 InetAddress |
3 | static InetAddress getByName(String host) 在给定主机名的情况下确定主机 IP 地址 |
4 | String getHostAddress() 返回 IP 地址字符串(文本表现) |
5 | String getHostName() 获取此 IP 地址的主机名 |
6 | static InetAddress getLocalHost() 返回本地主机 |
7 | String toString() 将此 IP 地址转换为String格式 |
七、测试demo
1、模拟客户端Client
通过 Socket 连接到服务器并发送一个请求,然后等待一个响应:
public class GreetingClient {
public static void main(String [] args) {
String serverName = args[0];
int port = Integer.parseInt(args[1]); // 端口号
try
{
System.out.println("连接到主机:" + serverName + " ,端口号:" + port);
// 创建一个套接字并将其连接到指定主机上的指定端口号
Socket client = new Socket(serverName, port);
// 获取套接字端口地址
System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
// 输出流(将会在服务器端输出)
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from " + client.getLocalSocketAddress());
//输入流
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("服务器响应: " + in.readUTF());
client.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
2、模拟服务器端Server
使用 Socket 来监听一个指定的端口
public class GreetingServer extends Thread {
private ServerSocket serverSocket;
public GreetingServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000); // 设置等待时间为10s
}
public void run() {
while(true) {
try {
System.out.println("等待远程连接,端口号为:" + serverSocket.getLocalPort() + "...");
// 监听并接收套接字连接
Socket server = serverSocket.accept();
System.out.println("远程主机地址:" + server.getRemoteSocketAddress());
// 输入流
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println(in.readUTF());
// 输出流(将会在客户端输出)
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("谢谢连接我:" + server.getLocalSocketAddress() + "\nGoodbye!");
server.close();
}catch(SocketTimeoutException s) { //如果时间耗尽
System.out.println("Socket timed out!");
break; // 886退出程序
}catch(IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String [] args) {
int port = Integer.parseInt(args[0]); // 端口号
try {
Thread t = new GreetingServer(port);
t.run(); // 开个线程
}catch(IOException e) {
e.printStackTrace();
}
}
}
接下来分俩个终端同时运行两个程序,首先本地1先输出“等待....”,接着我们在本地2运行程序输入主机名和同一个端口号6066,二者将会连接成功,并接着输出对应信息:
耶!客户端和服务器端的输入和输出流的连接,使得两个端口的信息成功交互输出!(红色部分就是交换内容)
写的中间出现了好多异常,也都在调试中一个个被消杀掉了。
Java异常,一生之敌 (T-T)