通信双方一方作为服务器等待客户提出请求并予以响应。客户则在需要服务时向服务器提 出申请。服务器一般作为守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的客户也 能及时得到服务。
【服务器】
package test.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TestServer {
public static void main(String[] args) {
try {
// 服务器socket并指定端口
ServerSocket server = new ServerSocket(9999);
// 接收服务器
Socket socket = server.accept();
// 获取服务器的输入流
BufferedReader reader
= new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 获取服务器的输出流
PrintWriter writer = new PrintWriter(socket.getOutputStream(),true);
// 来自键盘的输入数据
BufferedReader keyword =
new BufferedReader(new InputStreamReader(System.in));
while (true) {
// 当有输入数据的时候,ready返回true
if (reader.ready()) {
String str = reader.readLine();
System.out.println("Client: "+str);
}
// 捕获服务器另一个输入流,有数据,发送给client并打印
if (keyword.ready()) {
String keyStr = keyword.readLine();
writer.println(keyStr);
System.out.println("Server: " + keyStr);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
【客户端】
package test.socket;
import java.io.*;
import java.net.Socket;
public class TestClient {
public static void main(String[] args) {
try {
// 连接指定的服务器socket
Socket socket = new Socket("localhost",9999);
// 输入流
BufferedReader reader =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 输出流
PrintWriter writer =
new PrintWriter(socket.getOutputStream(), true);
// 键盘输入
BufferedReader keyword =
new BufferedReader(new InputStreamReader(System.in));
while (true) {
if (reader.ready()) {
String str = reader.readLine();
System.out.println("Server:" + str);
}
if (keyword.ready()) {
String key = keyword.readLine();
writer.println(key);
System.out.println("Client:" + key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
上一个例子,只能支持单一客户端和服务器通信,如果想要多个客户端与服务器保持通讯,则需要引入多线程
一个socket只能连接一个客户端和一个服务器,如果想要多个客户端与服务器通讯就需要多线程,这里多线程指的是,服务器方面每接收一个客户端的请求,就起一个子线程,具体操作就是serversocket.accept()方法作为子线程的参数传入,这样就实现多个客户端同时与服务器
实现runnable接口的服务器线程类
package test.socket.practice1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class SingleServer implements Runnable{
private Socket socket;
public SingleServer(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
boolean flag = true;
// 保证可以循环输入数据
while (flag) {
// 读取从客户端的信息
read();
// 读取从键盘的信息
BufferedReader keyword =
new BufferedReader(new InputStreamReader(System.in));
if (keyword.ready()) {
String keyStr = keyword.readLine();
System.out.println("服务器: "+keyStr);
write(keyStr);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 在服务器读取客户端发送过来的信息
* */
private void read() {
try {
BufferedReader reader =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
if (reader.ready()) {
String readStr = reader.readLine();
System.out.println("客户端" + readStr);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 把服务器写的信息发送给客户端
* */
private void write(String str) {
try {
PrintWriter writer =
new PrintWriter(socket.getOutputStream(),true);
writer.println("服务器:"+str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器类,开启线程,循环侦听客户端,每当一个客户端请求之后,就会启动一个线程与客户端相连
package test.socket.practice1;
import test.test.thread1;
import java.io.IOException;
import java.net.ServerSocket;
public class Server {
public static void main(String[] args) {
try {
// 设定监控端口,客户端可以寻找到
ServerSocket server = new ServerSocket(9999);
System.out.println("服务器开启...");
// 循环侦听客户端请求
while (true) {
SingleServer singleServer = new SingleServer(server.accept());
Thread t = new Thread(singleServer);
// 开启多线程
t.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
单线程客户端,可以多次启动(在idea中,默认是不能同时运行同一个类的,需要点击设置
勾中选择框)
package test.socket.practice1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class SingleClient {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 声明socket,并设定连接的服务器
Socket socket = new Socket("localhost",9999);
System.out.println("客户端开启...");
// 声明输入输出流
BufferedReader reader =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedReader key =
new BufferedReader(new InputStreamReader(System.in));
PrintWriter writer =
new PrintWriter(socket.getOutputStream(),true);
boolean flag = true;
while (flag) {
if (reader.ready()) {
String str = reader.readLine();
System.out.println("服务器:"+str);
}
if (key.ready()) {
String strKey = key.readLine();
writer.println(Thread.currentThread().getName() +": "+ strKey);
System.out.println("客户端" + Thread.currentThread().getName() +": "+ strKey);
// 客户端退出
if ("exit".equals(strKey)) {
flag = false;
socket.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
先启动server,在启动多个SingleClient ,可以实现多个客户端与server通讯
关于多线程socket编程,这里是有很多内容的,上面的这个只是一个简单的demo,还有很多问题,比如说,多个客户端的信息都可以发送到服务器,但是服务器是无法发送给客户端的,因为socket都在连接着,服务器只发送一个,不知道发给谁(也就说,服务器开启了多线程,但是从客户端看来,依旧只有一个服务器跟自己相连,服务器发送的消息,当只有一个客户端的时候是可以接收到的吗,但是多个客户端就不行。),其次,还有一种场景,服务器作为中间纽带,多客户之间互相通信(也就是聊天室类似的功能),我会一一补充
补充
Java面试知识点(七十一)Socket编程——多线客户端和服务器通讯