java网络通信
网络通信基本概念:
通信: 就是从一台机器上的一个软件,发送数据到另一台机器的一个软件上
先发送数据的软件:称为客户端
被动接收数据的软件**:称为服务端**
IP:IP在互联网中能唯一标识一台计算机,是每一台计算机的唯一标识(身份证);网络编程是和远程计算机的通信,所以必须先能定位到远程计算机;
端口:IP帮助解决此问题;一台计算机中可能有很多进程,具体和哪一个进程进行通信,这就得靠端口来识别;
ServerSocket和Socket
Socket
socket可以使一个应用从网络中读取和写入数据,不同计算机上的两个应用可以通过连接发送和接受字节流,当发送消息时,你需要知道对方的ip和端口,在java中,socket指的是java.net.Socket类。
Socket的构造函数
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 host为远程机器名称或ip地址,port为端口号。
若连接本地的Server,其端口号为8080,可以写成如下格式
new Socket(“localhost”, 8080);
127.0.0.1"也代表本机IP
重要的Socket API:
用最频繁的三个方法
- Accept()用于产生"阻塞",直到接受到一个连接,并且返回一个客户端的Socket对象实例。"阻塞"是一个术语,它使程序运行暂时"停留"在这个地方,直到一个会话产生,然后程序继续。
- getInputStream()获得网络连接输入,同时返回一个IntputStream对象实例,。
- getOutputStream()连接的另一端将得到输入,同时返回一个OutputStream对象实例。
注意:可将其两种输出流输入流包装成bufferinputstream和bufferoutputstream
注意:其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。
ServerSocket
Socket类代表一个客户端套接字,即任何时候连接到一个远程服务器应用时构建所需的socket。现在,要实现一个服务器应用,需要不同的做法。服务器需随时待命,因为不知道客户端什么时候会发来请求,此时,我们需要使用ServerSocket,对应的是java.net.ServerSocket类。
ServerSocket与Socket不同,ServerSocket是等待客户端的请求,一旦获得一个连接请求,就创建一个Socket示例来与客户端进行通信。
ServerSocket的构造方法
启动一个socket服务端(本质就是向操作系统注册一个端口号),端口号不可以超过65535
ServerSocket ss = new ServerSocket(1999);
,如果再其后加一个参数,把连接请求队列的长度设为 3。这意味着当队列中有了 3 个连接请求时,如果 Client 再请求连接,就会被 Server拒绝,因为服务器队列已经满了。我们使用的 serverSocket.accept()方法就是从队列中取出连接请求。
`ServerSocket ss = new ServerSocket(1999,3);`
服务端
服务器接收客户端请求步骤
1服务器建立通信 Serversocket
2服务器建立 Socket接收客户端连接建立
3IO输入流读取客户端发送的数据建立
4IO前出流向客户端发运数据消息
代码
package sockdemo;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServiceDemo {
public static void main(String[] args) throws Exception {
创建一个服务器端 Socket,即 Serversocket,指定绑定的端口,并监听此端口(本质就是向操作系统注册一个端口号)
ServerSocket ss = new ServerSocket(1999);
System.out.println("启动服务器");
// 调用 accept()方法开始监听,等待客户端的连接
Socket sc = ss.accept();
/**
* 从连接中接收数据
*/获取IO输入流
InputStream in = sc.getInputStream();
// 从输入流中拿数据
byte[] b = new byte[1024];
int num = in.read(b);
String string = new String(b,0,num);
System.out.println("收到客户端的消息:" + string);
/**
* 当接受到连接后,则可以向客户端发送数据
*/
//获取IO输出流
OutputStream out = sc.getOutputStream();
//从IO流输出数据
out.write("我是红红".getBytes());
//关闭输出流输入流还有连接
out.close();
in.close();
sc.close();
}
}
客户端
客户端向服务器发送请求可分为以下步骤:
1.创建一个Socket实例 ,设置通信的IP和端口
2.利用I/O流与服务器进行通信
3.关闭socket
package sockdemo;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) throws Exception {
// 创建 Socket通信,设置通信服务器的IP和Port
Socket sc = new Socket("127.0.0.1", 1999);
/**
* 发送数据
*/
//获取输出流
OutputStream out = sc.getOutputStream();
//发出数据
out.write("who are you?".getBytes());
/**
* 接收数据
*/
获取输入流
InputStream in = sc.getInputStream();
接受数据
byte[] b = new byte[10];
int num = in.read(b);
System.out.println(new String(b,0,num));
in.close();
out.close();
sc.close();
}
}
改造成常驻服务器
package cn.edu360.javase24.day13.socketdemo3;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo3 {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8080);
int i = 1;
while(true) {
Socket sc = ss.accept();
System.out.println("获得第"+i+"次连接了.....");
InputStream in = sc.getInputStream();
OutputStream out = sc.getOutputStream();
// 收客户端的第一个问题
byte[] b = new byte[1024];
int num = in.read(b);
System.out.println("收到客户端的第1个问题: " + new String(b,0,num));
// 回答第一个问题
out.write("我是宇宙无敌大美男".getBytes());
// 接收第二个问题
num = in.read(b);
System.out.println("收到客户端的第2个问题: " + new String(b,0,num));
// 回答第2个问题
out.write("我的理想女友是按住拉败笔".getBytes());
in.close();
out.close();
sc.close();
i++;
}
}
}
多线程
一、多线程是什么?为什么要用多线程?
介绍多线程之前要介绍线程,介绍线程则离不开进程。
首先 进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元;
线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。一个进程中至少有一个进程。
多线程:一个进程中不只有一个线程。
为什么要用多线程:
①、为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;
②、进程之间不能共享数据,线程可以;
③、系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;
④、Java语言内置了多线程功能支持,简化了java多线程编程。
二、线程的生命周期:
新建 :从新建一个线程对象到程序start() 这个线程之间的状态,都是新建状态;
就绪 :线程对象调用start()方法后,就处于就绪状态,等到JVM里的线程调度器的调度;
运行 :就绪状态下的线程在获取CPU资源后就可以执行run(),此时的线程便处于运行状态,运行状态的线程可变为就绪、阻塞及死亡三种状态。
等待/阻塞/睡眠 :在一个线程执行了sleep(睡眠)、suspend(挂起)等方法后会失去所占有的资源,从而进入阻塞状态,在睡眠结束后可重新进入就绪状态。
终止 :run()方法完成后或发生其他终止条件时就会切换到终止状态。
多线程的实现
- 写多线程程序的四部曲
- 1、将需要用多线程方式执行的逻辑,写入一个runnable实现类中(run方法中);
- 2、创建出这个runnable实现类的对象;
- 3、利用这个runnable对象构造出n个thread线程;
- 4、将这n个thread启动(thread.start());
代码演示
实现类写入runnable的演示
package cn.edu360.javase24.day13.thread.demo1;
public class Demo1 implements Runnable {
private String name;
public Demo1(String name) {
this.name = name;
}
public void setName(String name) { //可以通过这个方法向类中输入参数
this.name = name;
}
@Override //将线程要运行的代码放在该run方法中。
public void run() {
for (int i = 0; i < 6; i++) {
System.out.println("啊....."+name);
}
}
}
可以通过
public class Demo1Test {
public static void main(String[] args) {
Demo1 demo11 = new Demo1("张三");
Demo1 demo12 = new Demo1("李四");
Demo1 demo13 = new Demo1("王五");
Demo2 demo2 = new Demo2();
//demo1.run(); // 这样调,只是用单线程普通地执行一下这个run方法而已
// 构造一个线程,指定要执行的逻辑,利用这个runnable对象构造出n个thread线程;
Thread thread1 = new Thread(demo11);
Thread thread2 = new Thread(demo12);
Thread thread3 = new Thread(demo13);
Thread thread4 = new Thread(demo2);
Thread thread5 = new Thread(demo2);
// 这种调用,只是按顺序进行普通的方法调用,是在一个单线程中挨个执行的
/*thread1.run();
thread2.run();
thread3.run();
thread4.run();
thread5.run();*/
// 将这5个线程以多线程的方式同时运行
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
通过多线程改造常驻服务器
package cn.edu360.javase24.day13.thread.demo2;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 将对话服务器改造成多线程服务
* @author ThinkPad
*
*/
public class ThreadServerDemo {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10000);
int i=1;
while (true) {
Socket sc = ss.accept();
System.out.println("收到连接"+i);
Talk talk = new Talk(sc);
new Thread(talk).start();
i++;
}
}
}
talk类连Runnable接口
package cn.edu360.javase24.day13.thread.demo2;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* 用来封装线程中要执行的 对话逻辑(对话流程)
* @author ThinkPad
*
*/
public class Talk implements Runnable {
Socket sc;
public Talk(Socket sc) {
this.sc = sc;
}
@Override
public void run() {
try {
// 获取输入、输出流
InputStream in = sc.getInputStream();
OutputStream out = sc.getOutputStream();
//收第一个问题
byte[] b = new byte[1024];
int num = in.read(b);
System.out.println("收到客户端的问题1:" + new String(b,0,num));
// 回复第一个问题
out.write("我是宇宙无敌超级美少女战士".getBytes());
// 接收第二个问题
num = in.read(b);
System.out.println("收到客户端的问题2:" + new String(b,0,num));
// 回复第二个问题
out.write("我18岁".getBytes());
in.close();
out.close();
sc.close();
} catch (Exception e) {
System.out.println("发生异常了.......");
}