1.Sockete原理
- 是网络应用程序编程的接口和一种机制
- 用套接字中的相关函数来建立连接和完成通信
- Socket可以看成在两个程序进行通讯连接中的一个端点
2.基于TCP的Socket编程
利用TCP协议进行通信两个应用程序,有主从之分一个称为服务器程(Server),另外一个称为客户机程(Client)
(1)交互过程
- 服务器程序创建一个ServerSocket,然后调用accept方法等待客户来连接
- 客户端程序创建一个socket并请求与服务器建立连接
- 刚才建立了连接的两个socket在一个单独的线程上对话
- 服务器开始等待新的连接请求
(2)例子
Ⅰ.简单例子
①服务端
package com.server.cn;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author lqh 服务器
*/
public class Server {
public static void main(String[] args) {
Socket socket = null;
ServerSocket ss = null;
try {
// 1.启动服务器
ss = new ServerSocket(8888);
System.out.println("服务器已启动......");
// 2.等待客户端连接
socket = ss.accept();// 阻塞式方法,直到客户端链接进来,后续代码才能继续执行
System.out.println("有新的客户端连接进来......" + "\nIP地址:" + socket.getInetAddress() + ",端口号:" + socket.getPort());
// 5.接受客户端发送的消息
InputStream in = socket.getInputStream();// 字节流,一个字节一个字节读取
InputStreamReader inReader = new InputStreamReader(in);// 字符流一个字符一个字符读取
BufferedReader reader = new BufferedReader(inReader);// 字符流,一行一行读取数据
String message = reader.readLine();
System.out.println("客户端说:" + message);
// 6.给客户端发送消息
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.println("你好,我不在,请留言!!!");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8.关闭资源,释放资源
try {
if (socket != null) {
// Closing this socket will also close the socket's
// InputStream and OutputStream.
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (ss != null) {
// Closing this socket will also close the socket's
// InputStream and OutputStream.
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
②客户端
package com.server.cn;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author lqh 客户端
*/
public class Client {
public static void main(String[] args) {
Socket socket = null;
try {
// 3.连接服务器
socket = new Socket("localhost", 8888);
// 等价于
// Socket socket = new Socket("127.0.0.1", 8888);
// 4.给服务器发送消息
OutputStream out = socket.getOutputStream();// 字节输出流,只能一个字节一个字节输出数据
OutputStreamWriter outWriter = new OutputStreamWriter(out);// 字符流,一个字符一个字符输出数据
PrintWriter writer = new PrintWriter(outWriter);// 字符流,一行一行输出数据
writer.println("你好,在吗?");
writer.flush();
// 7.接受服务器消息
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 字符流,一行一行读取数据
String message = reader.readLine();
System.out.println("服务端说:" + message);
} catch (UnknownHostException e) {// 主机未知异常
e.printStackTrace();
} catch (IOException e) {// IO流异常
e.printStackTrace();
} finally {
// 8.关闭资源,释放资源
try {
if (socket != null) {
/*Closing this socket will also close the socket's InputStream and OutputStream.
* 所以不需要关闭输出流
*/
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
③运行结果
先打开服务端,再打开客户端
Ⅱ.服务端与客户端之间的聊天,当服务端输入“exit”时,结束对话
①服务端
package com.onetoone.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* @author lqh 实现客户端与服务器一对一循环聊天
*/
public class Server {
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
try {
// 1.启动服务器
server = new ServerSocket(8888);
System.out.println("服务端开启中。。。");
// 2.等待客户端连接
socket = server.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
Scanner scanner = new Scanner(System.in);
outer:while (true) {
// 5.接受客户端发送的信息
String message = reader.readLine();
// 判断客户端是否下线
if ("exit".equals(message.trim())) {
System.out.println("IP地址为:" + socket.getInetAddress() + ", 端口号为:" + socket.getPort() + "的客户端已下线!");
break outer;
} else {
System.out.println("客户端说:" + message);
}
// 6.给客户端发送消息
System.out.println("服务端,请输入:");
String text = scanner.nextLine();
writer.println(text);
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8.关闭资源、释放资源
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
②客户端
package com.onetoone.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
Socket client = null;
try {
// 3.连接服务器
client = new Socket("localhost", 8888);
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
Scanner scanner = new Scanner(System.in);
outer:while (true) {
// 4.给服务器发送消息
System.out.println("客户端,请输入:");
String text = scanner.nextLine();
// 判断如果客户端输入"exit",表客户端准备退出
writer.println(text);
writer.flush();
if ("exit".equals(text)) {
break outer;
}
// 7.接收服务器消息
String message = reader.readLine();
System.out.println("服务端说:" + message);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8.关闭资源、释放资源
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
③运行结果
Ⅲ.服务端与服务端之间的聊天
①服务端
package com.clientonetomore.test2;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
/**
* @author lqh 实现客户端循环聊天群聊,改进版
*/
public class Server {
// 用于保存所有客户端聊天的socket对象
static HashMap<Integer, Socket> socketMap = new HashMap<>();
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
try {
// 1.启动服务端
server = new ServerSocket(8888);
System.out.println("服务端开启...");
// 2.等待客户端连接
int num = 0;
while (true) {
socket = server.accept();
System.out.println(socket.getPort() + "客户端连接成功!");
socketMap.put(num++, socket);
ServerThread t = new ServerThread(socket);
t.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 8.关闭资源、释放资源
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (server != null) {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
②客户端
package com.clientonetomore.test2;
import java.io.IOException;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
Socket client = null;
try {
// 3.连接服务端
client = new Socket("localhost", 8888);
// 4.给其它客户端发送消息
new Client_sendThread(client).start();
// 7.接收其它客户端发来的消息
new Client_getThread(client).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
③服务端接收并转发信息的线程
package com.clientonetomore.test2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
public class ServerThread extends Thread {
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
// 5.接受客户端发送的信息
String message = reader.readLine();
System.out.println("客户端说:" + message);
// 给所有客户端转发收到的消息
HashMap<Integer, Socket> socketMap = Server.socketMap;
Collection<Socket> values = socketMap.values();
for (Socket socket : values) {
// 自己不用收到自己的消息
if (!this.socket.equals(socket)) {
PrintWriter clientWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
clientWriter.println(message);
clientWriter.flush();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
④客户端发送信息的线程
package com.clientonetomore.test2;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client_sendThread extends Thread {
private Socket socket;
public Client_sendThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
PrintWriter writer;
try {
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
Scanner scanner = new Scanner(System.in);
while (true) {
// 给其它客户端发送消息
System.out.println("客户端,请输入:");
String text = scanner.nextLine();
// 判断如果客户端输入"exit",表客户端准备退出
writer.println(text);
writer.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
⑤客户端接收信息的线程
package com.clientonetomore.test2;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client_sendThread extends Thread {
private Socket socket;
public Client_sendThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
PrintWriter writer;
try {
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
Scanner scanner = new Scanner(System.in);
while (true) {
// 给其它客户端发送消息
System.out.println("客户端,请输入:");
String text = scanner.nextLine();
// 判断如果客户端输入"exit",表客户端准备退出
writer.println(text);
writer.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
⑥运行效果