通信一定是基于软件结构的:
1.C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
2.B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构 常见浏览器有谷歌、火狐等、京东、淘宝。(开发中的重点,基于网页设计界面,界面效果可以更丰富)
网络通信的三要素。
1.协议
- 协议:计算机网络客户端与服务端通信必须事先约定和彼此遵守的规则,
2.IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。
IP地址用来给一个网络中的计算机设备做唯一的编号.
IPv4:4个字节,32位组成。
局域网,城域网,广域网(公网)
已经分配完了,不够用。
局域网:公司内部用。
公网:可以在任何地方访问。
IPv6:可以实现为所有设备分配IP
ipconfig:查看本机的IP
ping:检查本机与某个IP指定的机器是否联通,或者说是检测对方是否在线。
ping 空格 IP地址
ping
ping www.baidu.com
特殊的IP地址: 本机IP地址127.0.0.1、localhost
3.端口:端口号就可以唯一标识设备中的进程(应用程序)了
端口号:用两个字节表示的整数,它的取值范围是0~65535
0~1023之间的端口号用于一些知名的网络服务和应用,数据库MYSQL端口为3306;
普通的应用程序需要使用1024以上的端口号。
如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
利用协议+IP地址+端口号三元组合,
就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。
网络通信的分层和协议。
网络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。
应用层 :应用程序(QQ,浏览器),可能用到的协议(HTTP,FTP,SMTP) 通常程序员只需要关心这一层
------------------------------------------------------------------------------
传输层 :TCP/IP协议 UDP协议 计算机网络工程师需要精通的协议,有些技术我们也需要精通这一层协议,以后再学习!
---------------------------------------------------------
网络层 :IP协议
--------------------------------------------------------------
数据链路层 : 进入到硬件(网)
物理层
TCP/IP协议:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的安全的可靠的传输通信协议。
1.在通信之前必须确定对方在线并且连接成功才可以通信。
2.例如下载文件、浏览网页等(可靠传输)
UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的不可靠传输的协议。
1.直接发消息给对象,不管对方是否在线,发消息后也不需要确认。
2.无线(视频会议,通话),性能好,丢失一些数据!!
TCP通信系列技术:Socket通信(BIO),NIO , NIO2 , AIO , Netty
目标:Socket网络编程。
Socket(套接字,端口):端到端的通信,只要是基于Socket在通信,那么我们就是默认基于了TCP/IP协议通信的。
Socket网络编程是Java提供的基于TCP/IP的有连接的可靠传输协议。
需要提供一个客户端 和 一个服务端。
需求:客户端发送一个消息给服务端,服务端收到客户端的消息输出即可!!(一发一收)
Java是通过java.net.Socket类来实现的。
(1)先开发服务端
a.注册端口
-- public ServerSocket(int port):
ServerSocket serverSocket = new ServerSocket(9999);
b.等待客户端的请求连接得到一个Socket通信管道。
-- public Socket accept()
Socket socket = serverSocket.accept();
c.从Socket管道中得到字节输入流
-- public InputStream getInputStream()
InputStream is = socket.getInputStream();
d.从字节输入流中读取客户端发送来的消息。
(2)开发客户端
a.请求与服务端的Socket管道的连接。
-- public Socket(String host, int port)
Socket socket = new Socket("192.168.**.**1",9999);
b.从Socket管道中得到一个字节输出流。
-- public OutputStream getOutputStream()
OutputStream os = socket.getOutputStream();
c.用这个字节输出流写出数据到服务端。
通信是很严格的,客户端怎么发,服务端就怎么收,
客户端发了多少,服务端就只能接收多少!!
一发一收:分别为客户端和服务端
/**
目标:一发一收,服务器再响应接收成功的数据
*/
public class ClientDemo {
public static void main(String[] args) throws Exception {
// a.请求与服务端的Socket管道的连接。
Socket socket = new Socket("127.0.0.1",9989);
// b.从Socket管道中得到一个字节输出流。
OutputStream os = socket.getOutputStream();
// c.写数据出去
// 把字节输出流包装成打印流写数据给服务器
PrintStream ps = new PrintStream(os);
ps.println("我是客户端,我给你发了,约吗?");
ps.flush();
// 读取服务端响应的消息
// 从Socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null ;
if((line = br.readLine()) != null){
System.out.println(line);
}
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws Exception {
System.out.println("----服务器被启动----");
// a.注册端口得到监听客户端的对象:serverSocket
ServerSocket serverSocket = new ServerSocket(9989);
// b.通过ss对象等待客户端的请求连接得到一个Socket通信管道。
Socket socket = serverSocket.accept();
// c.从Socket管道中得到字节输入流
InputStream is = socket.getInputStream();
// d.从字节输入流中读取客户端发送来的消息。
// 把字节输入流管道改装成高级缓冲字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 开始按照行读取客户端发来的消息
String line = null ;
if((line = br.readLine()) != null){
System.out.println("服务端收到:"+line);
}
// 响应一个接收成功的消息发送回给客户端。
// 从Socket管道中得到一个字节输出流
OutputStream os = socket.getOutputStream();
// 把字节输出流包装成打印流
PrintStream ps = new PrintStream(os);
ps.println("服务器已收到您的消息 200 Ok!");
ps.flush();
// 服务器死的晚一点,等客户端收到后再死亡
Thread.sleep(2000);
}
}
目标:客户端可以反复的发消息,服务端可以反复的收消息。
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo {
public static void main(String[] args) throws Exception {
// a.请求与服务端的Socket管道的连接。
Socket socket = new Socket("127.0.0.1",9999);
// b.从Socket管道中得到一个字节输出流。
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
while(true){
// c.创建一个扫描器反复接收用户键盘的输入
Scanner scn = new Scanner(System.in);
String msg = scn.nextLine();
// 写一行消息给服务端
ps.println(msg);
ps.flush();
}
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) {
try{
System.out.println("----服务器被启动----");
// a.注册端口得到监听客户端的对象:serverSocket
ServerSocket serverSocket = new ServerSocket(9999);
// b.通过ss对象等待客户端的请求连接得到一个Socket通信管道。
Socket socket = serverSocket.accept();
// c.从Socket管道中得到字节输入流
InputStream is = socket.getInputStream();
// d.从字节输入流中读取客户端发送来的消息。
// 把字节输入流管道改装成高级缓冲字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 开始按照行读取客户端发来的消息
String line = null ;
while((line = br.readLine()) != null){
System.out.println("服务端收到:"+line);
}
}catch (Exception e){
// 说明客户端下线了
System.out.println("客户端离线了!!");
}
}
}
服务端可以接收无数个客户端的消息,可以反复发,反复收(群聊)
解决方案:如果服务端需要接收多个客户端的连接,服务端必须实现一个客户端一个线程处理!
引入多线程。
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo {
public static void main(String[] args) throws Exception {
// a.请求与服务端的Socket管道的连接。
Socket socket = new Socket("192.168.**.**1",9999);
// b.从Socket管道中得到一个字节输出流。
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
while(true){
// c.创建一个扫描器反复接收用户键盘的输入
Scanner scn = new Scanner(System.in);
String msg = scn.nextLine();
// 写一行消息给服务端
ps.println(msg);
ps.flush();
}
}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) {
try{
System.out.println("----服务器被启动----");
// a.注册端口得到监听客户端的对象:serverSocket
ServerSocket serverSocket = new ServerSocket(9999);
while(true){
// b.通过ss对象等待客户端的请求连接得到一个Socket通信管道。
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+"上线,来了老弟!");
// 每次接收到一个客户端的通信请求后服务端就为这个管道创建一个线程来处理它
new MyServerReaderThread(socket).start();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
class MyServerReaderThread extends Thread{
private Socket socket;
public MyServerReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try{
// c.从Socket管道中得到字节输入流
InputStream is = socket.getInputStream();
// d.从字节输入流中读取客户端发送来的消息。
// 把字节输入流管道改装成高级缓冲字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 开始按照行读取客户端发来的消息
String line = null ;
while((line = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress()+"说了:"+line);
}
}catch (Exception e){
System.out.println(socket.getRemoteSocketAddress()+"下线了!");
}
}
}
客户端:
目标:文件上传。 客户端的文件,服务端的存储目录。
服务器的目录:D:\腾讯图片资源服务器
上传图片:D:\图片资源\meinew01.jpg
import java.io.*;
import java.net.Socket;
public class ClientDemo {
/** (1)定义发送图片的路径。 */
public static final String SRC_FILE = "D:\\图片资源\\m01.jpg";
public static void main(String[] args) {
try {
/** (2)请求服务端的Socket通信管道连接 */
Socket socket = new Socket("192.168.**.***", 8888);
/** (3)定义一个字节输入流读取源文件 */
InputStream is = new FileInputStream(SRC_FILE);
/** (4)从Socket管道中得到一个字节输出流*/
OutputStream os = socket.getOutputStream();
/** (5)从字节输入流中读取文件数据写出到字节输出流管道*/
int len = 0 ;
byte[] buffer = new byte[1024];
while((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
os.flush();
socket.shutdownOutput(); // 通知服务端我已经发送完毕,请你不用再等我了!
// 接收上传后的结果
// 读取服务端响应的消息
// 从Socket通信管道中得到一个字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null ;
if((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
/**
目标:服务端开发
服务器开发分析:
(1)注册端口
(2)定义一个循环接收客户端的请求交给一个独立的多线程(接收无数客户端)
(3)在线程中处理这个客户端读取文件数据。
(4)从Socket管道中得到一个字节输入流。
(5)定义一个字节输出流与服务器的目录连通。
(6)从字节输入流中读取数据写出到字节输出流
*/
public class ServerDemo {
// 定义服务器的文件目录
public static final String DEST_FILE = "D:\\腾讯图片资源服务器\\";
public static void main(String[] args) {
try{
System.out.println("----服务器被启动----");
/** (1)注册端口 */
ServerSocket serverSocket = new ServerSocket(8888);
while(true){
/** (2)定义一个循环接收客户端的请求交给一个独立的多线程(接收无数客户端) */
Socket socket = serverSocket.accept();
new MyServerReaderThread(socket).start();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
class MyServerReaderThread extends Thread{
private Socket socket;
public MyServerReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try{
/** (3)在线程中处理这个客户端读取文件数据。 */
/** (4)从Socket管道中得到一个字节输入流。*/
InputStream is = socket.getInputStream();
/**(5)定义一个字节输出流与服务器的目录连通。 */
OutputStream os = new FileOutputStream(ServerDemo.DEST_FILE+ UUID.randomUUID().toString()+".jpg");
/**(6)从字节输入流中读取数据写出到字节输出流 */
int len = 0 ;
byte[] buffer = new byte[1024];
while((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
os.close();
// 响应数据给客户端
// 响应一个接收成功的消息发送回给客户端。
// 从Socket管道中得到一个字节输出流
// 把字节输出流包装成打印流
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("服务器已收到您的文件! 200 Ok!");
ps.flush();
// 服务器死的晚一点,等客户端收到后再死亡
Thread.sleep(2000);
}catch (Exception e){
System.out.println(socket.getRemoteSocketAddress()+"下线了!");
}
}
image.png
编写程序满足上图要求:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
try {
//与服务端连接
Socket socket = new Socket("127.0.0.1", 22228);
//把输出流包装成高级流
PrintStream ps = new PrintStream(socket.getOutputStream());
//提示客户输入格式
System.out.println("请输入您的用户名和密码,格式如:admin=123456");
//创建扫描器,等待客户输入
Scanner sc = new Scanner(System.in);
String date = sc.nextLine();
//把用户名和密码打印出来
ps.println(date);
ps.flush();
//响应服务端
BufferedReader bf = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("收到服务端响应:"+bf.readLine());
bf.close();
ps.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try {
//建立端口与服务端连接
ServerSocket serverSocket = new ServerSocket(22228);
while (true){
//开始接收客户端的socket管道请求
Socket socket = serverSocket.accept();
//创建一个线程来处理
new ReadThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
public class ReadThreads extends Thread {
private Socket socket;
public ReadThreads(Socket socket) {
this.socket = socket; }
//从socket管道包装成高级缓冲流管道
@Override
public void run() {
BufferedReader br;
{
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//读取客户登录信息:ademin = 12345
String line = br.readLine();
//建立集合存储客户信息
Map<String,String> nameAndPsw = new HashMap<>();
//从文件读取信息
BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\IdeaProjects\\users.txt")));
String ln = null;
while ((ln = bfr.readLine())!=null){
String[] name = ln.split(",");
nameAndPsw.put(name[0],name[1]);
}
// System.out.println(nameAndPsw);
//比较客户输入信息与文件信息是否一样
String[] names = line.split("=");
String name1 = names[0];
String pwd1 = names[1];
PrintStream ps = new PrintStream(socket.getOutputStream());
if(nameAndPsw.containsKey(name1)){
if(nameAndPsw.containsValue(pwd1)){
ps.println("登录成功");
}else{
ps.println("密码错误");
}
}else {
ps.println("用户名错误");
}
ps.flush();
Thread.sleep(2000);
ps.close();
br.close();
bfr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}