技术:
- 网络编程Socket
- 多线程Thread
- 数据传递
具体实现:
网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。更具体的说,网络编程就是两个或多个程序之间的数据交换。就如我们平常的QQ聊天、打开网易APP刷新界面等,就会有新的数据从它们的服务器端里面获取出来。它们的APP就相当于客户端。还有我们打开一个浏览器,就可以得到一个对应的网页,如我们在浏览器输入一个www.baidu.com,就会显示一个百度的网页,实际上这个网页就是由一些基本数据组成的,而这些基本数据是由百度的服务器获取出来的。
- 客户端(Client):给我们用户要用的信息就是客户端,如手机APP 浏览器 桌面QQ
-
服务器端(Server):就是用来存储数据或者说是处理数据的地方
我们从客户端访问服务器端就是通过url地址来访问。这里可以知道服务器其实不存储数据,真正存储数据是由数据库来完成的。
- URL:统一的资源定位(就是大家都用url来找到服务器里面的资源)
我们通常用的url如:http://www.baidu.com/search?code=andriod&type=1
在这里 - http/https:传输协议 Tcp/IP协议
- www.baidu.com :域名 表示一台网络中的电脑
- ip地址:唯一标识一台网络中的计算机,每一台电脑都有自己独一的ip地址(ipv4 ipv6)
- ip地址共有32位,四个部分每个部分8个字节。
如:ip地址172.20.10.15 <-> www.baidu.com
由于ip地址的数字有点多和长,不利于记忆。因此开发了域名系统(Domain Name System,DNS)用来将人类易于记忆的主机名(如www.12345.com)转换为数字Internet地址(如208.201.243.99)。当Java程序访问网络时,他们需要同时处理数字地址和相应的主机名。这些方法由java.net.InetAddress类提供。 - search 对应的后台程序文件 php java jsp
- ? 表示运行这个程序需要传递的参数
- code=andriod code是服务器端规定的字段
- & 如果有多个参数使用&符号链接
/**
* ip地址:唯一标识某一台电脑
* 端口号:标识这台电脑上运行的某个程序(进程)80
*
* 数据传递:Tcp/IP协议
* TCP:面向连接的 安全的 打电话
* UDP:面向无连接的 不安全 但快
*/
- 读取数据:服务器读取 客户端读取
BufferedReader->InputStreamReader->socket.getInputStream();
读取终端
BufferedReader->InputStringReader->System.in
- 输出:客户端输出 服务器端输出
BufferedWriter->OutputStreamWriter->socket.getOutputStream();
PrintStream->socket.getOutputStream();
注意:实现客户端和服务器端传递数据: 注意端口号必须相同 先运行Server 再运行Client。
//模拟客户端
class Client{
public static void main(String[] args)throws IOException{
//1.创建用于通信的socket
// 指明和谁通信:ip地址 端口号
Socket socket = new Socket("192.168.137.1",8989);
//接收服务器端的数据
BufferedReader br= new BufferedReader(new InputStreamReader(socket.getInputStream()));
//socket.getInputStream();
//向服务器端发送数据
//socket.getOutputStream();
String line = null;
while ((line = br.readLine()) != null){
System.out.println(line);
}
//客户端向服务器发送数据
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("hello ");
socket.shutdownOutput();
}
}
//模拟服务器端
class Server{
public static void main(String[] args)throws IOException {
//1.创建服务器端serverSocket
ServerSocket ss= new ServerSocket(8989);
//2.获取连接的客户端的socket
Socket clientSocket = ss.accept();
//3.向客户端发送数据 对于字符的输入输出Reader / Writer
//BufferedWriter br = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
PrintStream ps = new PrintStream(clientSocket.getOutputStream());
ps.println("你已登陆成功");
clientSocket.shutdownOutput();//告诉客户端输出完毕
//4.服务器端接收客户端发来的数据
BufferedReader br= new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String line = null;
while ((line = br.readLine()) != null){
System.out.println(line);
}
}
}
运行结果:
这里就成功实现了客户端向服务器端输入“Hlleo”,服务器端向客户端输出“登陆成功”。实现了数据的相互传输。
我们上面这个程序代码不能实现从服务器端手动的输入输出。接下啦我们来实现服务器端的手动的输入。将输入的内容发送给客户端可以一直发。
class MyClinet{
public static void main(String[] args){
//连接服务器端的socket
Socket socket = null;
try {
socket = new Socket("192.168.137.1",8888);
//接收服务器端信息
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line =null;
while ((line= br.readLine()) !=null){
System.out.println(line);
}
//从键盘输入数据 发送给终端
} catch (IOException e) {
e.printStackTrace();
System.out.println("网络出错 请重新输入");
}finally {
//关闭连接
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class MyServer{
public static void main(String[] args){
//创建ServerSocket
try (ServerSocket ss= new ServerSocket(8888)){
//监听客户端
Socket socket = ss.accept();
//从终端接收数据
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
//获取向客户端输出数据的输出流
PrintStream ps = new PrintStream(socket.getOutputStream());
String line = null;
while ((line=keyin.readLine())!= null){
//发送给客户端
ps.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
手动收入内容 发送
socket扮演的是客户端
ServerSocket扮演的是服务器端
-
客户端 1.文本 2.图片 3.视频 4.音频 服务器端 只要是数据都可在两者之间进行传递
运行结果:
在这里就实现了从服务器端的手动输入,将输入的内容发送给客户端。
前面我们说了在客服端和服务器端可以实现 1.文本 2.图片 3.视频 4.音频等等的传递。
实现客户端向服务器端上传图片
/**
* 客户端向服务器端发送图片
* post上传
*/
class Client{
public static void main(String[] args) throws IOException {
//连接服务器 获取socket
Socket socket = new Socket("192.168.137.1",8080);
//创建服务器端对应的输入流 用于接收服务器端发来的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
//向服务器端发送文件(图片)
//1.将文件写入到内存里
String path ="C:\\Users\\DAMEI\\Desktop\\1.jpg";
FileInputStream fis = new FileInputStream(path);
//2.创建字节流 outputStream
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] buf = new byte[1024];
int len = -1;
while ((len = fis.read(buf)) != -1){
bos.write(buf,0,len);
}
socket.shutdownOutput();
}
}
class Server{
public static Iterable<? extends Socket> sockets;
public static void main(String[] args) throws IOException {
//创建服务器端的ServerSoket
ServerSocket ss = new ServerSocket(8080);
//监听客户端连接
//当有客户端来连接这个服务器 就可以得到对应的socket
//当没有客户端来连接 服务器一直在这里等待
Socket socket = ss.accept();
//创建客户端对应的输出流 用于向这个客户端发送数据
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("连接成功 可以发数据了!");
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String path ="D:\\android study\\yjx1\\src\\main\\java\\swu\\yjx\\day12media\\picture";
FileOutputStream fos = new FileOutputStream(path);
byte[] buf = new byte[1024];
int len = -1;
while((len = bis.read(buf)) != -1){
fos.write(buf,0,len);
}
}
}
在这个程序代码里,我的电脑运行出来没有把图片给输出来,不知道是路径还是哪里错了,还得找找。基本上的代码就是这样的。
我们前面的实现只是图片和服务器手动的输入,并没有实现不仅能从客户端输入,也能从服务器端输入。
实现多线程对聊:
* 客户端和服务器端都可以随意的发送内容
* 从终端输入
*
* 实际就是客户端和服务器端在对聊
* 客户端:
* 主线程:接收终端的输入 将终端输入发送给服务器端
* 子线程:接收服务器端发过来的数据
*
*服务器端:
* 主线程:接收终端输入 将终端输入发送给客户端
* 子线程:接收客户端发过来的数据
客服端:
class Client1{
public static void main(String[] args) throws IOException {
//连接socket
Socket socket = new Socket("127.0.0.1",8888);
//用一个子线程处理服务器端的数据
new Thread(new ClientThread(socket)).start();
//主线程处理终端输入 发送给服务器
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
PrintStream ps = new PrintStream(socket.getOutputStream());
String line= null;
while ((line=keyin.readLine())!=null){
ps.println(line);
}
}
}
/**
* 创建一个子线程处理客户端接收服务器端的数据
*/
class ClientThread implements Runnable{
private Socket socket;
//保存操作的socket对象
public ClientThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
//获取服务器短的输入流对象
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//读取数据
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(br != null) {
br.close();
}
if(socket != null){
socket.close();
}
} catch (IOException e) {
System.out.println("网络出错,请重新登录!");
System.exit(-1);
}
}
}
}
服务器端:
class Server1{
public static void main(String[] args) throws IOException {
ServerSocket ss= new ServerSocket(8888);
//获取客户端的socket
Socket socket = ss.accept();
//创建子线程 处理客户端输入信息
new ServerThread(socket).start();
//终端输入流对象
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
//客户端输出流对象
PrintStream ps = new PrintStream(socket.getOutputStream());
//读取终端的输入 将输入对象发送客户端
//主线程
String line = null;
while ((line = keyin.readLine()) !=null){
ps.println(line);
}
}
}
/**
*创建一个子线程用于处理服务器端接收客户端的数据
* */
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line =null;
while ((line= br.readLine())!= null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络异常 请重新登陆");
System.exit(-1);
}finally {
//关闭输入输出流
//关闭对应的socket连接
try {
if (br!=null){
br.close();
}
if (socket!=null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
实现群聊 Client和ClientThread代码和对聊不变
主要在Server里添加数组和监听客户端连接
服务器端:
class Server{
//保存每一个连接过来的socket对象
public static ArrayList<Socket> sockets =new ArrayList<>();
public static void main(String[] args) throws IOException {
ServerSocket ss= new ServerSocket(6666);
//不停的等待客户端来连接
while (true) {
Socket socket = ss.accept();
//当有客户端连接过来了就保存
sockets.add(socket);
//开启一个线程处理每个客户端的输入
new ServerThread(socket).start();
}
}
}
/**
*创建一个子线程用于处理服务器端接收客户端的数据
* */
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line =null;
while ((line= br.readLine())!= null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络异常 请重新登陆");
System.exit(-1);
}finally {
//关闭输入输出流
//关闭对应的socket连接
try {
if (br!=null){
br.close();
}
if (socket!=null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端:
class Client2{
public static void main(String[] args) throws IOException {
//连接socket
Socket socket = new Socket("192.168.137.1",6666);
//用一个子线程处理服务器端的数据
new Thread(new ClientThread(socket)).start();
//主线程处理终端输入 发送给服务器
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
PrintStream ps = new PrintStream(socket.getOutputStream());
String line= null;
while ((line=keyin.readLine())!=null){
ps.println(line);
}
}
}
/**
* 创建一个子线程处理客户端接收服务器端的数据
*/
class ClientThread implements Runnable{
private Socket socket;
//构造方法 保存数据对象
public ClientThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//读取数据
String line = null;
while ((line=br.readLine())!=null){
//群发消息
//遍历Server维护的数组
for(Socket socket: Server.sockets){
//向socket对应的客户端发送消息
PrintStream ps = new PrintStream(socket.getOutputStream());
}
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络异常 请重新登陆");
e.printStackTrace();
}finally {
try {
if (br != null)
{br.close();
} if (socket != null){
socket.close();}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
总结:以上就是Java中的基本的网络编程大概。在这里我们不仅实现了图片的上传、手动的从服务器端输入,还实现了客户端和服务器端的对聊及群聊。在这里也运用到了很多我们以前所讲到的东西。如:
- 多线程Thread
- while和for循环
- run方法
- 继承extends
- 接口implements
- 处理异常throws
- 构造方法
等等。