今天复习了一遍Socket通信,发现还是有很多不知道的地方,在这里做一个记录,便于以后需要的时候查看。
Socket通信主要依据两个类ServerSocket和Socket
ServerSocket 是用做服务端的,其中常用方法有server.accept(),此方法是一个阻塞性质的方法,当接收到连接时结束阻塞。Socket client = server.accpet();用来获取客户端的连接状态,当客户端连接后会返回给我们一个Socket套接字,通过此套接字可以完成与客户端的联系。
Socket是用来表示连接状态的,客户端可以通过Socke socket = new Socket("host",portNum);来创建一个连接指定IP的指定端口的套接字,通过此套接字来完成与目标服务器之间的通信,常用的方法有getInputStream()返回一个输入流,通过侦听此流可以获取服务器朝客户端发送的信息,getOutputStream()返回一个输出流,通过此输出流可以用来朝服务器传输数据。
下面这个例子实现了多个客户端之间的通信,但是没有完成单对单的通信,服务端以广播的形式朝所有客户端发送信息。
在这里说明一点值得注意的,也是自己以前经常犯的错误,在我们用输出流写完数据之后要写换行符(“”\n“”),否则流不会将你的数据发送出去,数据还保留在缓冲区中,即使调用了flush()也没用,这点尤其是在死循环发送客户端输出数据时尤为重要。
服务端代码如下:
package day05;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* Socket编程服务端。
* @author Administrator
*/
public class SocketServer {
final static List<Socket> clients = new ArrayList<>();
public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket(10000);
System.out.println("服务端已启动");
while (true) {
final Socket client = server.accept();
clients.add(client);// 将连接存入集合中
System.out.println("发现新连接,已连接:" + clients.size());
// 开启线程监听每一个client
new Thread() {
public void run() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
while (true) {
String msg = reader.readLine();
if (msg != null) {
System.out.println(msg);
if (msg.equals("bye")) {
break;
}
for (int i = 0; i < clients.size(); i++) {
if(clients.get(i)!=client){
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(clients.get(i).getOutputStream()));
writer.write(client.getInetAddress()+":"+client.getPort()+"说:"+msg + "\n");
writer.flush();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clients.remove(client);
reader.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
}.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
server.close();
server = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端代码:客户端主要是开启一个线程去持续读取服务端的信息,然后自己在另一个线程(我这里是主线程)中发送自己想发送的数据。
package day05;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* Socket客户端
* @author Administrator
*/
public class SocketClient {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("localhost", 10000);
System.out.println("客户端已启动!");
final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//接收数据
new Thread() {
public void run() {
while(true){
String msg;
try {
msg = reader.readLine();
System.out.println(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
};
}.start();
// 构建IO流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 建立键盘输入:
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入发送消息内容:");
String msg = scanner.nextLine();
bw.write(msg + "\n");
bw.flush();
if (msg.equals("bye")) {
System.out.println("bye~");
break;
}
}
scanner.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}