1.Socket类
- 套接字(Socket)是一个抽象类,应用程序可以通过它发送或接收数据;就像操作文件那样可以打开、读写和关闭。套接字允许应用程序将 I/O 应用于网络中,并与其他应用程序进行通信。网络套接字是 IP 地址与端口的组合。
- 使用Socket连接服务器的过程包含以下4个基本的步骤:
(1)创建Socket
(2)打开连接到Socket的输入/输出流
(3)按照一定协议对Socket执行读写操作
(4)关闭Socket
- 创建Socket连接
Socket(String host, int port)throws UnknownHostException, IOException;
Socket(InetAddress address, int port)throws UnknownHostException, IOException;
Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException;
Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException;
- 第一种方法用得最多。除去第一种不带参数的之外,其它构造函数会尝试建立与服务器的连接。如果失败会抛出IOException异常。如果成功,则返回Socket对象。
- InetAddress是一个用于记录主机的类,其静态方法getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。
- Socket(String host, int port, InetAddress localAddress, int localPort)构造函数的参数分别为目标IP、目标端口、绑定本地IP、绑定本地端口。
注意:在套接字建立的时候,如果远程主机不可访问,这段代码就会阻塞很长时间,直到底层操作系统的限制而抛出异常。所以一般在套接字建立后要设置一个超时时间。
Socket socket = new Socket(...);
socket.setSoTimeout(10000);
// 单位为毫秒
- Socket方法:
getInetAddress(); 远程服务端的IP地址
getPort(); 远程服务端的端口
getLocalAddress() 本地客户端的IP地址
getLocalPort() 本地客户端的端口
getInputStream(); 返回与调用的套接字相关联的输入流
getOutStream(); 返回与调用的套接字相关联的输出流
//最后两个方法很重要
- Socket状态
isClosed(); //连接是否已关闭,若关闭,返回true;否则返回false
isConnect(); //如果曾经连接过,返回true;否则返回false
isBound(); //如果Socket已经与本地一个端口绑定,返回true;否则返回false
isConnected(); //判断Socket的连接状态
2.ServerSocket类:
- 定义:服务器端用来监听特定端口上客户端的连接,也可以发送信息。
- 构造函数:
ServerSocket()throws IOException;
ServerSocket(int port)throws IOException;
ServerSocket(int port, int backlog)throws IOException;
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException;
- port:服务端要监听的端口;backlog:客户端连接请求的队列长度;bindAddr:服务端绑定IP
- 使用ServerSocket连接客户端的过程包含以下4个基本的步骤
(1)创建ServerSocket
(2)监听客户端的连接(accept()方法)
(3)打开连接到ServerSocket的输入/输出流
(4)按照一定协议对ServerSocket执行读写操作
注意:Socket需要自己关闭,但是ServerSocket不需要自己关闭
Socket与ServerSocket的关系
3.实例:
多线程在网络中的运用,代码流程图:
流程图
因为一个线程只能干一件事,不能同时接受键盘的输入又接受客户端(服务器端)输入的数据,使用必须开启一个子线程帮助主线程完成这两个任务,代码如下:
- 客户端
class Client{
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8080);
// 用一个子线程处理服务器端数据
new Thread(new CilentThread(socket)).start();
// 从终端接收数据,发给客户端
BufferedReader keyIn = new BufferedReader(new InputStreamReader(System.in));
PrintStream ps = new PrintStream(socket.getOutputStream());
String line = null;
while ((line = keyIn.readLine()) != null){
ps.println(line);
}
}
}
//创建一个客户端的子线程,接收客户端发送的数据
class CilentThread implements Runnable{
private Socket socket;
public CilentThread (Socket socket){
this.socket = socket;
}
@Override
public void run(){
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络出错,请检查");
System.exit(-1);
}finally {
try {
if (br != null) {
br.close();
}
if (socket != null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 服务器端;
class Server{
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8080);
Socket socket = ss.accept();
// 创建一个子线程,处理客户端输入数据
new ServerThread(socket).start();
// 接收终端的输入(字符)
BufferedReader keyIn = new BufferedReader(new InputStreamReader(System.in));
// 客户端的输出流
PrintStream ps = new PrintStream(socket.getOutputStream());
// 读取终端的输入,并输出给客户端
String line = null;
while ((line = keyIn.readLine()) != null) {
ps.println(line);
}
}
}
//创建一个子线程接收客户端的数据
class ServerThread extends Thread{
private Socket socket = null;
public ServerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络异常,请重新登陆");
// 断开子线程
System.exit(-1);
}finally {
try {
if (br != null){
br.close();
}
if (socket != null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}