一:想要看懂该java代码,需要掌握以下知识点
环境:eclipse,idea
- 进程的创建和启动
@Override public void run() { //这里放你的想要线程执行的代码 } }; new Thread(){}.start();//调用start()方法,启动线程,执行run()方法的代码 ```
- 输入输出流 不做过多介绍!
- socket网络通信
客户端Socket的工作过程包含以下四个基本的步骤:
1.创建 Socket:根据指定服务端的IP地址或端口号构造 Socket 类对象。若服务器端 响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
2.打开连接到 Socket 的输入/出流: 使用 getInputStream()方法获得输入流,使用
getOutputStream()方法获得输出流,进行数据传输
3.按照一定的协议对 Socket进行读/写操作:通过输入流读取服务器放入线路的信息 (但不能读取自己放入线路的信息),通过输出流将信息写入线程。
4.关闭 Socket:断开客户端到服务器的连接,释放线路
Socket s = new
Socket(“192.168.40.165”,9999);//表示服务端所在的电脑的IP为(192.168.40.165),端口号大于1000,基本没事,端口号为9999
OutputStream out = s.getOutputStream();
out.write(" hello".getBytes());
s.close();
注:
1.该代码的客户端和服务端可以在不同的电脑执行,可以多台电脑运行同一份客户端代码
2.获取本机IP的方式:
import org.junit.Test;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class test {
@Test
public void test() throws UnknownHostException {
System.out.println(InetAddress.getLocalHost());
}
}
//结果:admin/169.254.239.139
服务器程序的工作过程包含以下四个基本的步骤:
1.调用 ServerSocket(int port) :创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求。
2.调用 accept():监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象。
3.调用 该Socket类对象的getOutputStream() 和getInputStream ():获取输出流和输入流,开始网络数据的发送和接收。
4.关闭ServerSocket和Socket对象:客户端访问结束.关闭通信套接字。
注:
1.ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口 中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字 连接的ServerSocket对象。
2.所谓“接收”客户的套接字请求,就是accept()方法会返回一个 Socket 对象
ServerSocket ss = new ServerSocket(9999); //端口号对应,才能正确接收客户端的请求
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();
4.其余的一些java的基本语法
二:废话不多少,直接上代码!
客户端
package socket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* Created by HeTingwei on 2017/12/9.
* 多线程客户端
*/
public class Client1 {
private Socket socket;
private static final int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度
public static void main(String[] args) {//169.254.239.139
Scanner in=new Scanner(System.in);
System.out.println("是否要连接服务武器Y or N?");
String s = in.next();
if(s.equals("Y")) {
Client1 client = new Client1();
new Thread() {//开启一个接受数据的线程
@Override
public void run() {
super.run();
InputStream in;
try {
in = client.getSocket().getInputStream();
byte[] b;
while (true) {
b = new byte[MESSAGE_SIZE];
in.read(b);
System.out.println("接收到服务端消息:" + new String(b));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
OutputStream out = null;
while (true) { //循环发消息,遇到end结束
String str = in.nextLine();
//异常处理
try {
out = client.getSocket().getOutputStream();//向客户端发送
out.write(str.getBytes());
out.flush();
if (str.equals("end")) {
System.exit(0);//关闭客户端
}
} catch (IOException e) {
e.printStackTrace();
}
}
}else {
System.out.println("服务终止");
}
in.close();
}
public Client1() {//构造器
try {
socket = new Socket("169.254.239.139", 45678);
if (socket.isConnected() == true) {
System.out.println("连接成功");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public Socket getSocket() {
return socket;
}
}
服务端
服务端进程包括两个线程类:接受服务端的消息的线程,给服务端发消息的线程
还有一个List集合,存放已连接客户端类
package socket;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* Created by HeTingwei on 2017/12/9.
* 多线程服务器,实现多客户端聊天
*/
public class Server {
List<ReceiveThread> receiveList = new ArrayList<>();//存放已连接客户端类
private final static int MESSAGE_SIZE = 1024;//每次允许接受数据的最大长度
int num = 0;//客户端编号
public static void main(String[] args) {
new Server();
}
//服务端处理逻辑
Server() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(45678);//用来监听的套接字,指定端口号
while (true) {
Socket socket = serverSocket.accept();//监听客户端连接,阻塞线程
System.out.println("连接上客户端:" + num);
//在其他线程处理接收来自客户端的消息
ReceiveThread receiveThread = new ReceiveThread(socket, num);
receiveThread.start();
receiveList.add(receiveThread);
//有客户端新上线,服务器就通知其他客户端
String notice="有新客户端上线,现在在线客户端有:客户端:";
for (ReceiveThread thread : receiveList) {
notice = notice + " " + thread.num;
}
for (ReceiveThread thread : receiveList) {
new SendThread(thread.socket, notice).start();
}
num++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
//接受消息的线程(同时也有记录对应客户端socket的作用)
class ReceiveThread extends Thread {
int num;
Socket socket;//客户端对应的套接字
boolean continueReceive = true;//标识是否还维持连接需要接收
public ReceiveThread(Socket socket, int num) {
this.socket = socket;
this.num = num;
try {
//给连接上的客户端发送,分配的客户端编号的通知
socket.getOutputStream().write(("你的客户端编号是" + num).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
super.run();
//接收客户端发送的消息
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
byte[] b;
while (continueReceive) {
b = new byte[MESSAGE_SIZE];
inputStream.read(b);
b = splitByte(b);//去掉数组无用部分
//发送end的客户端断开连接
if (new String(b).equals("end")) {
continueReceive = false;
receiveList.remove(this);
//通知其他客户端
String message = "客户端" + num + "连接断开\n" +"现在在线的有,客户端:";
for (ReceiveThread receiveThread : receiveList) {
message = message + " " + receiveThread.num;
}
System.out.println(message);
for (ReceiveThread receiveThread : receiveList) {
new SendThread(receiveThread.socket, message).start();
}
} else {
try {
String[] data = new String(b).split(" ", 2);//以第一个空格,将字符串分成两个部分
int clientNum = Integer.parseInt(data[0]);//转换为数字,即客户端编号数字
//将消息发送给指定客户端
for (ReceiveThread receiveThread : receiveList) {
if (receiveThread.num == clientNum) {
new SendThread(receiveThread.socket, "客户端"+num+"发消息:"+data[1]).start();
System.out.println("客户端" + num + "发送消息到客户端" + receiveThread.num + ": " + data[1]);
}
}
} catch (Exception e) {
new SendThread(socket, "输入错误,请重新输入").start();
System.out.println("客户端输入错误");
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {//关闭资源
if (inputStream != null) {
inputStream.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//发送消息的线程
class SendThread extends Thread {
Socket socket;
String str;
public SendThread(Socket socket, String str) {
this.socket = socket;
this.str = str;
}
@Override
public void run() {
super.run();
try {
socket.getOutputStream().write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
//去除byte数组多余部分
private byte[] splitByte(byte b[]) {
int i = 0;
for(;i<b.length;i++) {
if (b[i] == 0) {
break;
}
}
byte[] b2 = new byte[i];
for (int j = 0; j <i ; j++) {
b2[j] = b[j];
}
return b2;
}
}
三运行示例:
发消息示例:你要向哪个客户端发消息<空格>消息
其余的不懂得可以在评论区探讨