概述
java.net.Socket
代表两台机器之间网络连接的对象
客户端必须要认识服务器,服务器必须要认识所有的客户端。
- 如何建立客户端与服务器之间的初始连接
- 如何传送信息到服务器
- 如何接收来自服务器的信息
建立连接
要创建Socket连接,必须知道服务器在哪里以及用哪个端口来收发数据,也就是IP地址和端口号
try {
Socket socket = new Socket("192.168.20.135", 5000);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
常用端口
- 80 # HTTP网页服务器,HTTPS使用443端口,FTP使用20端口
- 110 # POP3邮件服务器
- 25 # SMTP邮局交换服务器
- 23 # Telnet服务器
0~1023
端口保留给已知的特定服务使用,可以从1024~65535
之间挑选。
数据读写
BufferedReader
使用 BufferedReader 从 Socket 上读取数据
Socket socket = new Socket("192.168.20.135", 5000);
InputStreamReader stream = new InputStreamReader(socket.getInputStream()); //从 Socket 上取得输入串流
BufferedReader reader = new BufferedReader(stream);
String msg = reader.readLine();
reader.close();
PrintWriter
用 PrintWriter 写数据到 Socket 上
Socket socket = new Socket("192.168.20.135", 5000);
PrintWriter writer = new PrintWriter(socket.getOutputStream()); //连接 Socket 和 String 流
writer.println("message to send"); //带换行
writer.print("another message");
writer.close();
Socket Server
一对Socket,一个用于等待用户请求的ServerSocket,一个用于与用户通信的Socket
ServerSocket
try {
ServerSocket serverSocket = new ServerSocket(4242);
while(true){ //服务器进入无穷循环等待客户端的请求
Socket socket = serverSocket.accept(); //此方法会停下来等待要求到达之后再继续
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.println("Welcome...");
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
工作方式
1 服务器应用程序对特定端口创建出ServerSocket
ServerSocket serverSocket = new ServerSocket(4242);
这会让服务器应用程序开始监听来自 4242 端口的客户端请求
2 客户端对服务器应用程序建立Socket连接
Socket socket = new Socket("192.168.20.135", 9000);
客户端根据服务器的IP地址和端口号建立连接
3 服务器创建出与客户端通信的新的Socket
Socket socket = serverSocket.accept();
accept() 方法会在等待用户的Socket连接时闲置着,当用户连上来时,此方法会返回一个不同端口上的新的Socket以便与客户端通信。如此ServerSocket可以空出来等待其他的用户。
取得所有连线
List<PrintWriter> clientOutputStreams = new ArrayList<>(); //定义客户端输出流列表
try {
ServerSocket serverSocket = new ServerSocket(9000); //监听连接到9000端口的所有客户端
while(true){
Socket clientSocket = serverSocket.accept();
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream()); //取得客户端输出流
clientOutputStreams.add(writer); //添加到输出流列表
//启动监听客户端输入流线程
Thread readClientMsg = new Thread(new ClientHandler(clientSocket));
readClientMsg.start();
}
} catch (IOException e) {
e.printStackTrace();
}
群发消息
public void sendToAll(String msg){
Iterator<PrintWriter> itClient = clientOutputStreams.iterator(); //利用迭代器进行遍历
while(itClient.hasNext()){
PrintWriter writer = itClient.next();
writer.println(msg);
writer.flush();
}
}
取得客户端消息
利用多线程循环监听客户端发来的消息,如果获取到消息就发送给所有用户
线程在取得客户端连接时就启动
Socket clientSocket = serverSocket.accept();
Thread readClientMsg = new Thread(new ClientHandler(clientSocket));
readClientMsg.start();
class ClientHandler implements Runnable{
BufferedReader reader;
Socket socket;
public ClientHandler(Socket clientSocket){
//绑定客户端并取得输入流
this.socket = clientSocket;
try {
InputStreamReader is = new InputStreamReader(socket.getInputStream());
reader = new BufferedReader(is);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
String msg;
//循环监听输入流,取到消息后发送给所有客户端
try {
while((msg = reader.readLine()) != null){
sendToAll(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket Client
发送消息给服务器
Socket socket;
PrintWriter writer;
private void setSocket(){
try {
socket = new Socket("192.168.20.135", 4242);
writer = new PrintWriter(socket.getOutputStream());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendMsg(String msg){
writer.println(msg);
writer.flush();
}
接收服务器消息
结合以上发送消息合并代码
BufferedReader reader;
private void setSocket(){
try {
socket = new Socket("192.168.20.135", 4242);
writer = new PrintWriter(socket.getOutputStream()); //定义输出流,通过发送按钮发送消息给服务器
InputStreamReader streamReader = new InputStreamReader(socket.getInputStream()); //定义输入流
reader = new BufferedReader(streamReader); //绑定到缓存输入流,利用多线程循环循环查询服务器消息并接收
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
开启新线程循环接收服务器消息
Thread readMsg = new Thread(new ReadMsg());
readMsg.start();
private class ReadMsg implements Runnable{
@Override
public void run() {
String msg;
try {
while((msg = reader.readLine()) != null){
//获取到服务器信息
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}