ServerSocket和Socket类:
ServerSocket是服务器用的“网络插口”可以定义,端口号等;Socket是客户端用的"网络插口",用来连接制定IP、端口等;
下面直接用代码和其中的注释演示一个网络通信的例子:在线聊天室;
package Test01;
/**
* 群聊:Server:接收某一位成员的消息 转发给其他成员(收到消息才发出去 收发两个动作是单线程执行) Client:发送自己的消息 接受别的消息
* (收发互不干扰 收消息与发消息应该使用两个单独的线程)
*
* 服务器端 ServerSocket 1. 指定服务器这个程序在本机上的端口号(0~65535 0~1023(系统预先装订的程序占用了)) 2.
* 监听客户端的连接 3. 交互
*
*/
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
private static final int PORT = 20001;
// 定义一个集合来放我们的客户端的socket对象,当做用户列表
private static List<Socket> onLineUsers = new ArrayList<>();
public static void main(String[] args) {
ServerSocket server = null;
try {
// 用能传入端口的构造器穿件ServerSocket对象
server = new ServerSocket(PORT);
System.out.println("服务器已经准备好了 客户端可以连接了");
} catch (IOException e) {
// 这个异常是因为如果我们定义的端口已经被其他软件占用了,那么我们就不能成功连接
System.out.println("端口被占用了!");
}
while (true) {
Socket client = null;
try {
/*
* 监听客户端的连接请求,accept这个方法监听时,处于阻塞状态, 也就是说程序会停在这先不往下执行,直到监听到了连接请求
*/
client = server.accept();
} catch (IOException e) {
System.out.println("监听客户端时出现异常");
}
synchronized (onLineUsers) {
// 这部分代码需要同步,因为我们的表单这个集合是共享变量;
onLineUsers.add(client);
}
ServerThread serverThread = new ServerThread(client, onLineUsers);
Thread t = new Thread(serverThread);
t.start();
}
}
}
//写
class ServerThread implements Runnable {
private Socket client;
private List<Socket> onLineUsers;
private DataInputStream dis;
private String clientIp;
//定义构造器,参数有Socket对象,和表单集合
public ServerThread(Socket client, List<Socket> onLineUsers) {
this.client = client;
this.onLineUsers = onLineUsers;
this.clientIp = client.getInetAddress().getHostAddress();
try {
this.dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
System.out.println("服务器取得用户输入流时出现异常,用户已下线");
// 用户下线,我们把它从表单中删除
synchronized (onLineUsers) {
onLineUsers.remove(client);
}
}
}
@Override
public void run() {
String msg = null;
DataOutputStream dos;
System.out.println("欢迎用户" + clientIp + "进入聊天室");
// 不断地收消息
while (true) {
if (dis != null) {
try {
msg = dis.readUTF();
} catch (IOException e) {
System.out.println("服务器读取用户消息时出现异常,用户已下线");
synchronized (onLineUsers) {
onLineUsers.remove(client);
}
break;
}
if (msg == null || msg.equals("")) {
// 不转发消息
} else {
// 转发消息,需要遍历表单中每一个用户
for (Socket s : onLineUsers) {
//因为这个时候表单里已近add了此用户,此客户端没必要对自己也发一次消息,所以我们进行一次判断;如果不是再进行下一步操作
if (s != client) {
try {
//Socket有getOutputStream的方法,我们创建对象并调用此方法将msg输入进流
dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF(clientIp + ":" + msg);
} catch (IOException e) {
synchronized (onLineUsers) {
onLineUsers.remove(s);
}
}
}
}
}
}
}
}
}
客户端如下:
package Test01;输出结果:(我们先运行服务器,)
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
/**
* 客户端:Socket
* 发消息 收消息
*/
public class Client {
private static DataInputStream dis ;
private static DataOutputStream dos;
public static void main(String[] args) {
Socket client =null;
try {
//创建对象的时候发起连接请求,其中localhost指本机ip地址,20001指端口,在这里我客户端和服务器都在我的电脑上运行;
client = new Socket("localhost",20001);
} catch (IOException e) {
System.out.println("连接服务器失败");
}
try {
//创建数据流输入输出的两个对象
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
System.out.println("服务器端异常 客户端无法获取输入输出流!");
}
//开启线程
RecMsg rec = new RecMsg(dis);
SendMsg send = new SendMsg(dos);
Thread recMsg = new Thread(rec);
Thread sendMsg = new Thread(send);
recMsg.start();
sendMsg.start();
}
}
class RecMsg implements Runnable{
private DataInputStream dis ;
public RecMsg(DataInputStream dis) {
this.dis = dis;
}
@Override
public void run() {
//收消息----输入流
while(true){
String msg = null;
try {
//读入数据,读入在流中的数据,注意read的格式应与服务器呼应
msg = dis.readUTF();
} catch (IOException e) {
System.out.println("服务器端出现异常 用户读取消息失败");
}
System.out.println(msg);
}
}
}
class SendMsg implements Runnable{
private DataOutputStream dos;
private Scanner scanner = new Scanner(System.in);
public SendMsg(DataOutputStream dos) {
this.dos = dos;
}
@Override
public void run() {
//发消息----输出流
while(true){
String msg2Send = scanner.next();
if(msg2Send!=null&&(!msg2Send.equals(""))){
try {
dos.writeUTF(msg2Send);
} catch (IOException e) {
System.out.println("服务器端出现异常 用户发送消息失败");
}
}
}
}
}
服务器显示:
服务器已经准备好了 客户端可以连接了
欢迎用户127.0.0.1进入聊天室
欢迎用户127.0.0.1进入聊天室
client1:
你好(此为输入)
127.0.0.1:嗯嗯,你好呀
client2:
127.0.0.1:你好
嗯嗯,你好呀(此为输入)
自己和自己聊天(笑),下次用swing等工具加上图形界面就更好玩啦