Socket多客户端通信
服务端怎么写
- 使用循环,一直监听端口(会阻塞),监听到一个socket开一个新的线程
- 记录得到的客户端
- 功能:
- 输入流接受从客户端发送的消息
- 输出流,将输入流得到的字符串输出给记录的所有客户端
服务端代码
package com.fs.chats;
/**
* @email zjx9527@foxmail.com,
* @author:Zhou_jx,
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server extends Thread {
//ServerSocket server = null;
//初始化socket对象
Socket socket = null;
//初始化一个socket集合,记录连接的客户端
static Socket[] sockets = new Socket[10];
//记录连接客户端的数量
static int x = 0;
/*public Server(int port) {
try {
server = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}
}*/
//有参构造方法,得到监听的客户端并记录
public Server(Socket socket) {
this.socket = socket;
sockets[x] = socket;
x++;
}
@Override
public void run() {
super.run();
try {
//for (Socket socket:sockets){
//System.out.println(socket.toString());
System.out.println("准备连接...");
//socket = server.accept();
System.out.println(socket.getInetAddress().getHostAddress() + "连接成功...");
//初始化输入流
InputStream in = socket.getInputStream();
//记录输入的长度
int len;
//初始化字符串
String str = null;
byte[] buf = new byte[1024];
//读取
while ((len = in.read(buf)) != -1) {
//System.out.println("客户端: " + new String(buf, 0, len));
//记录从客户端发来的消息,并对不同得客户端信息进行标记
str = socket.toString()+":" + new String(buf, 0, len);
System.out.println(str);
//调用发送消息的线程
new sendMessThread(str).start();//连接并返回socket后,再启用发送消息线程
}
//}
} catch (IOException e) {
e.printStackTrace();
System.out.println("会话结束!!");
}
}
class sendMessThread extends Thread {
//定义字符串
String str;
//有参构造,初始化需要转发的内容
public sendMessThread(String str) {
this.str = str;
}
@Override
public void run() {
super.run();
OutputStream out;
try {
//遍历所有记录的客户端
for (Socket socket : sockets) {
//进行判定,防止抛空指针异常
if (socket != null) {
//初始化输出流
out = socket.getOutputStream();
//输出
out.write(str.getBytes());
out.flush();//清空缓存区的内容
/*try {
out.close();
} catch (IOException e) {
System.out.println("会话结束");
}*///这里得注释掉,因为一边接收一边发送,如果现在关闭OutputStream会报SocketException异常
}
}
} catch (IOException e) {
System.out.println("会话结束!");
}
}
}
//函数入口
public static void main(String[] args) {
// 1.创建服务器端ServerSocket对象,并为服务器端注册端口号
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(1234);
} catch (IOException e) {
e.printStackTrace();
}
//循环监听端口,阻塞中,监听到一个创建一个新线程
while (true) {
Socket socket = null;
try {
assert serverSocket != null;
socket = serverSocket.accept();
new Server(socket).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端怎么写
- 建立连接
- 功能
- 输出流:通过Scanner从控制台输入字符串进行输出
- 输入流:接受来自于服务端得转发的数据
客户端代码
package com.fs.chats;
/**
* @email zjx9527@foxmail.com,
* @author:Zhou_jx,
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client extends Thread {
//定义一个Socket对象
Socket socket = null;
//有参构造连接对应得服务端
public Client(String host, int port) {
try {
//需要服务器的IP地址和端口号,才能获得正确的Socket对象
socket = new Socket(host, port);
System.out.println("成功连接端口"+port);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
//客户端一连接就可以写数据个服务器了
new sendMessThread().start();
super.run();
try {
// 读Sock里面的数据
InputStream s = socket.getInputStream();
byte[] buf = new byte[1024];
int len;
while ((len = s.read(buf)) != -1) {
System.out.println(new String(buf, 0, len));
}
} catch (IOException e) {
System.out.println("会话结束");
}
}
//往Socket里面写数据,需要新开一个线程
class sendMessThread extends Thread{
@Override
public void run() {
super.run();
//写操作
Scanner scanner=null;
OutputStream os= null;
try {
scanner=new Scanner(System.in);
os= socket.getOutputStream();
String in;
do {
in=scanner.next();
os.write((""+in).getBytes());
os.flush();
} while (!in.equals("bye"));
} catch (IOException e) {
System.out.println("服务端已经关闭!");
}
/*scanner.close();
try {
assert os != null;
//os.close();
} catch (IOException e) {
e.printStackTrace();
}*/
}
}
//函数入口
public static void main(String[] args) {
//需要服务器的正确的IP地址和端口号
Client clientTest=new Client("127.0.0.1", 1234);
clientTest.start();
}
}
写的过程中出现的异常
socket 只要在 io流close的情况下 自动关闭,但是后面还需要使用就会抱这个异常,所以需要将close()语句注释掉
第一个正常,但是后续的线程会报空指针异常,是因为记录客户端对象的集合是属性,需要用static修饰,保证不会应为新创建对象而把集合中的对象刷新掉,(看了半天,原来是这么个简单的问题。。。)