网络编程基础
1.1 计算机网络
所谓计算机网络,就是把分布在不同区域的计算机与专门的外部设备用通信线路相互连接成一个规模大,而且功能强的网络系统,从而使得计算机之间可以相互传递信息,共享数据、软件等资源。
网络分类 :按照地理范围:广域网( Internet) ,城域网( 一个城市、多个城市,长城宽带),局域网(机房)
1.2 网络编程
所谓网络编程(不是网站编程),指的就是在同一个网络中不同机器之间的通信。
1.3 网络参考模型
OSI参考模型:包括七层:[物理层]、[数据链路层]、[网络层]、[传输层]、[会话层]、[表示层]和[应用层]
TCP/IP参考模型:包括四层:
1.链路层(数据链路层/物理层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡
2.网络层:处理分组在网络中的活动,比如分组的选路。
3.传输层:主要为两台主机上的应用提供端到端的通信。
4.应用层(应用层/表示层/会话层):负责处理特定的应用程序细节。
1.4 通信协议
需要通信的设备之间需要实现相同的通信协议
TCP/IP网络分层 :链路层、网络层、传输层、应用层
通信协议分类:
网络层IP协议:IPV4和IPV6,互联网协议
传输层协议:TCP和UDP
应用层协议:HTTP、FTP、SMTP、POP3
TCP协议:TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。数据大小无限制。建立连接的过程叫三次握手,断开叫四次断开。
UDP协议:UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是TCP/IP参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,每个包的大小64Kb。
IP协议:[Internet Protocol]网际协议,能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。IP协议中包含一块非常重要的内容就是为计算机分配了一个唯一标识即IP地址。
计算机之间通信
IP地址
IP地址指的是互联网地址(Internet Protocol Address ) ,是联网设备与互联网之间的唯一标识,在同一个网段中,IP地址是唯一的。
IP地址是数字型的,是一个32位的整数,通常将其分成4个8位的二进制数,每8位之间用圆点隔开, 每个8位整数可以转换为一个0~255的十进制整数,例如:202.9.128.88
分为IPV4和IPV6
查看ip:ipconfig
IPV4地址分类
A类:保留给政府结构,1.0.0.1 ~ 126.255.255.254
B类:分配给中型企业,128.0.0.1 ~ 191.255.255.254
C类:分配给任何需要的个人,192.0.0.1 ~ 223.255.255.254
D类:用于组播,224.0.0.1 ~ 239.255.255.254
E类:用于实验,240.0.0.1 ~ 255.255.255.254
回环地址:127.0.0.1,指本机,一般用于测试使用,使用ping命令测试: ping 127.0.0.1
IP地址可以唯一的确定网络上的一个通信实体,但一个通信实体可以有多个通信程序同时提供网络服务,此时还需要使用端口。
2.2 端口
数据的发送和接收都需要通过端口出入计算机,端口号用于唯一标识通信实体上进行网络通讯的程序,同一台机器上不能两个程序占用同一个端口。
端口号的取值范围:0~65535
查看端口占用: netstat -ano
端口分类:
公认端口:0~1023
注册端口:1024~49151
动态或私有端口:49152~65535
常用端口:
mysql:3306
oracle:1521
tomcat:8080
web服务器(http):80
ftp服务器:21
SMTP 25
POP3 110
3.1 InetAddress类
Java提供了InetAddress类来代表ip地址,是对ip地址的抽取和封装,有两个子类:Inet4Address,Inet6Address,分别表示IPv4和IPv6
常用方法:**
//1.获取主机:主机名称和ip地址
/**
* static InetAddress getLocalHost()
返回本地主机。
*/
InetAddress id1 = null;
try {
id1 = InetAddress.getLocalHost();
//USER-VG9EDR1SST/10.31.165.42
System.out.println(id1);
} catch (UnknownHostException e) {
// 未知的主机
e.printStackTrace();
}
//2.获取ip地址的字符串表示形式
/**
* String getHostAddress()
返回 IP 地址字符串(以文本表现形式)。
*/
String str1 = id1.getHostAddress();
System.out.println(str1);//10.31.165.42
//3.获取主机名
/**
* String getHostName()
获取此 IP 地址的主机名。
*/
String str2 = id1.getHostName();
System.out.println(str2);
//4.根据主机或者ip地址获取InetAddress对象
/**
* static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
*/
try {
InetAddress id2 = InetAddress.getByName("10.31.165.42");
///10.31.165.42
System.out.println(id2);
InetAddress id3 = InetAddress.getByName("www.baidu.com");
//www.baidu.com/115.239.211.112
System.out.println(id3);
} catch (UnknownHostException e) {
e.printStackTrace();
}
//5.根据主机或者ip地址获取所有InetAddress对象
/**
* static InetAddress[] getAllByName(String host)
*/
try {
InetAddress[] arr = InetAddress.getAllByName("www.baidu.com");
for(InetAddress address:arr) {
//www.baidu.com/115.239.210.27
System.out.println(address.toString());
//115.239.210.27
System.out.println(address.getHostAddress());
//www.baidu.com
System.out.println(address.getHostName());
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
基于TCP的网络编程
概念
TCP:Transmission Control Protocol,传输控制协议,基于字节流的传输层通信协议。
特点:
1.面向连接
2.安全、可靠
3.传输数据大小限制,一旦连接建立,双方可以按统一的格式传输任意多的数据。
4.效率低
TCP的三次握手
a.客户端向服务端发送一个请求
b.服务端收到请求后,回客户端一个响应
c.客户端向收到服务端的响应后,回服务端一个确认信息
基于TCP的Socket(套接字)通信模型
Socket是操作系统提供的一种底层的通信机制,Java仅仅是对底层的socket的一种封装。供开发人员方便使用。
案例:客户端发送消息,服务端接收消息
先启动服务端在启动客户端
1 建立服务器 指定端口号
2 注册监听
3 获取输入输出流
4 处理数据
5 关闭
ServerSocket 服务端程序
package com.qf0812.Demo3Net;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 2019/8/1216:26
* <p>
* 未知的事情 永远充满变数
*/
public class TcpServer {
public static void main(String[] args)throws Exception {
ServerSocket list = new ServerSocket(12345);
System.out.println("服务器已启动!");
Socket co = list.accept();
InputStream is = co.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String data = br.readLine();
System.out.println("客户端说:"+data);
br.close();
co.close();
list.close();
}
}
Client 客户端程序
1建立客户端 指定服务器地址和端口号
2获取输入输出流
3 发送数据
4 关闭
package com.qf0812.Demo3Net;
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 2019/8/1216:26
* <p>
* 未知的事情 永远充满变数
*/
public class TcpClient {
public static void main(String[] args)throws Exception {
Socket sock = new Socket("127.0.0.1",10086);
OutputStream os = sock.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write("好久不见!");
bw.close();
sock.close();
}
}
案例2:客户端发送消息,服务端回复消息
package com.qf0812.Demo3Net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 2019/8/1216:53
* <p>
* 未知的事情 永远充满变数
*/
public class TcpServer1 {
public static void main(String[] args)throws Exception {
ServerSocket listener = new ServerSocket(10086);
System.out.println("服务器已启动。。。");
Socket socket = listener.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bo = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String data = br.readLine();
System.out.println("客户端说:"+data);
bo.write("十分想念");
bo.newLine();
bo.flush();
br.close();
bo.close();
socket.close();
listener.close();
}
}
客户端:
package com.qf0812.Demo3Net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 2019/8/1216:53
* <p>
* 未知的事情 永远充满变数
*/
public class TcpClient1 {
public static void main(String[] args)throws Exception {
Socket sc = new Socket("127.0.0.1",7890);
BufferedReader bs = new BufferedReader(new InputStreamReader(sc.getInputStream()));
BufferedWriter bo = new BufferedWriter(new OutputStreamWriter(sc.getOutputStream()));
bo.write("好久不见!");
bo.newLine();
bo.flush();
String data1 =bs.readLine();
System.out.println("服务器说:"+data1);
bs.close();
bo.close();
sc.close();
}
}
案例3:客户端上传文件到服务端【以图片为例】
package com.qf0812.Demo3Net;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 2019/8/1217:44
* <p>
* 未知的事情 永远充满变数
*/
public class TcpServer2 {
public static void main(String[] args) throws Exception{
//1 建立服务器
ServerSocket socket = new ServerSocket(8899);
//2实现监听
System.out.println("服务器已启动!");
Socket sc= socket.accept();
System.out.println("连接成功");
//获取输入输出流
BufferedInputStream iso = new BufferedInputStream(sc.getInputStream()) ;
BufferedWriter isc = new BufferedWriter(new OutputStreamWriter(sc.getOutputStream()));
BufferedOutputStream isc2 = new BufferedOutputStream(new FileOutputStream("D:\\那些年我们回不去的青春.jpg"));
byte [] buf = new byte[1024];
int len =0;
while ((len = iso.read(buf))!=-1){
isc2.write(buf,0,len);
}
System.out.println("上传成功!");
iso.close();
isc.close();
isc2.close();
sc.close();
socket.close();
}
}
客户端
package com.qf0812.Demo3Net;
import java.io.*;
import java.net.Socket;
/**
* 2019/8/1217:55
* <p>
* 未知的事情 永远充满变数
*/
public class TcpClient2 {
public static void main(String[] args)throws Exception {
Socket socket = new Socket("127.0.0.1",8899);
BufferedReader bo = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedOutputStream bi = new BufferedOutputStream(socket.getOutputStream());
BufferedInputStream bs = new BufferedInputStream(new FileInputStream("E:\\图片\\昨天.jpg"));
byte [] buf = new byte[1024];
int len =0;
while ((len=bs.read(buf))!=-1){
bi.write(buf,0,len);
}
System.out.println("复制完成!");
bs.close();
bo.close();
bi.close();
socket.close();
}
}
多个客户端和一个服务端通信【线程版本】
服务器端:
package com.qf0812.Demo3Net;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 2019/8/1218:58
* <p>
* 未知的事情 永远充满变数
*/
// 聊天服务器
//多线程
public class TcpServer3 {
public static void main(String[] args)throws Exception {
//1创建ServletSocket
ServerSocket listener = new ServerSocket(7890);
System.out.println("聊天服务器启动了。。。。");
//2监听
try{
while (true){
Socket socket = listener.accept();
new ChatThread(socket).start();
}
}catch (Exception e){
System.out.println("聊天室结束了!");
}finally {
listener.close();
}
}
}
线程类:
package com.qf0812.Demo3Net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 2019/8/1221:34
* <p>
* 未知的事情 永远充满变数
*/
public class ChatThread extends Thread {
private Socket socket;
public ChatThread(Socket socket) {
this.socket = socket;
}
public void run(){
BufferedReader br = null;
if(socket!=null){
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true){
String data = br.readLine();
System.out.println(socket.getInetAddress()+"说:"+data);
if(data.equals("exit")||data.equals("over")) {
System.out.println(socket.getInetAddress() + "退出了聊天系统!");
break;
}
}
} catch (Exception e) {
System.out.println(socket.getInetAddress()+"异常退出!");
}finally {
try {
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端(采用与服务器交互时的客户端版本 案例2的服务端):
package com.qf0812.Demo3Net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
/**
* 2019/8/1216:53
* <p>
* 未知的事情 永远充满变数
*/
public class TcpClient1 {
public static void main(String[] args)throws Exception {
Socket sc = new Socket("127.0.0.1",7890);
BufferedReader bs = new BufferedReader(new InputStreamReader(sc.getInputStream()));
BufferedWriter bo = new BufferedWriter(new OutputStreamWriter(sc.getOutputStream()));
bo.write("好久不见!");
bo.newLine();
bo.flush();
String data1 =bs.readLine();
System.out.println("服务器说:"+data1);
bs.close();bo.close(); sc.close();
}
}
TCP实现注册登录
1 服务器负责启动 并创建properties 保存用户名与密码并传给注册和登入线程
2注册 定义变量接受properties 并编写注册用户启动的ServerSocket; 获取用户信息 并判断properties文件是否有对应的信息
并执行对应的操作 (如果properties内没有该键 ---!properties.containsKey(arr[0]),就把现在的用户名名添加进去如果有了则返回
用户名已存在信息)并返回给客户对应的信息(注册成功/注册失败)
3登入 定义变量接受properties 并编写登入用户启动的ServerSocket; 获取用户信息 并判断properties文件是否有对应的信息
并执行对应的操作 (如果有该用户名 则进一步判断密码是否匹配 如果匹配则返回登入成功 反之则失败 如果
没有改用户名则返回没有该用户 登入失败 )
4 创建客户端程序
服务端程序
package com.qf0812.Demo3Net.注册登入;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
/**
* 2019/8/148:04
* <p>
* 未知的事情 永远充满变数
*/
public class TcpServer {
public static void main(String[] args) {
Properties properties = new Properties();
String f = "userinfo.properties";
File file = new File(f);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
properties.load(new FileReader(file));
} catch (IOException e) {
e.printStackTrace();
}
}
new LoginThread(properties).start();
new RegThread(properties).start();
}
}
客户端程序:
package com.qf0812.Demo3Net.注册登入;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
/**
* 2019/8/148:26
* <p>
* 未知的事情 永远充满变数
*/
public class TcpClient {
public static void main(String[] args) throws Exception{
reg();
login();
}
//注册方法
public static void reg()throws Exception{
doAction(9999);
}
//登入方法
public static void login()throws Exception{
doAction(8888);
}
//连接服务端程序
public static void doAction(int port)throws Exception{
Socket socket = new Socket("127.0.0.1",port);
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.next();
System.out.println("请输入密码!");
String pasword = sc.next();
bw.write(name+"#"+pasword);
bw.newLine();
bw.flush();
String data =br.readLine();
System.out.println("服务器回复:"+data);
bw.close();
br.close();
socket.close();
}
}
具体代码:
注册线程:
package com.qf0812.Demo3Net.注册登入;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
/**
* 2019/8/1320:52
* <p>
* 未知的事情 永远充满变数
*/
public class RegThread extends Thread{
private Properties properties;
public RegThread(Properties properties){
this.properties=properties;
}
public void run(){
//注册
//1 创建ServletSocket
BufferedReader br = null;
BufferedWriter bw = null;
Socket socket = null;
ServerSocket listener = null;
try {
listener = new ServerSocket(9999);
//侦听
System.out.println("注册线程启动了!");
socket = listener.accept();
br = new BufferedReader(new InputStreamReader(socket.getInputStream();));
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String data = br.readLine();
String [] arr = data.split("#");
if(arr!=null){
if(!properties.containsKey(arr[0])){
properties.setProperty(arr[0],arr[1] );
bw.write("注册成功!");
bw.newLine();
bw.flush();
// 保存
FileWriter fw = new FileWriter("username.properties");
properties.store(fw, " ");
fw.flush();
fw.close();
}else{
bw.write("用户名已存在");
bw.newLine();
bw.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
bw.close();
socket.close();
listener.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
登入程序:
package com.qf0812.Demo3Net.注册登入;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
/**
* 2019/8/1321:11
* <p>
* 未知的事情 永远充满变数
*/
public class LoginThread extends Thread{
private Properties properties;
public LoginThread(Properties properties) {
this.properties = properties;
}
public void run(){
ServerSocket Listener = null;
Socket socket =null;
BufferedWriter bw = null;
BufferedReader br =null;
try {
Listener = new ServerSocket(8888);
System.out.println("登入线程已经启动!");
socket = Listener.accept();
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String data = br.readLine();
String [] arr = data.split("#"); //"zhangsan#123456"
if(arr!=null){
String username = arr[0];
String password = arr[1];
if(!properties.containsKey(username)){
bw.write("登入失败, 用户名不存在");
bw.newLine();
bw.flush();
}else{
String pwd = properties.getProperty(username);
if(password.equals(pwd)){
bw.write("登入成功");
bw.newLine();
bw.flush();
}else {
bw.write("密码错误");
bw.newLine();
bw.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
bw.close();
socket.close();
Listener.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
扩展 :聊天程序 服务器转发给对应的客户端* 数据转发
服务端:
package com.qf;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/*
* wgy 2019/8/12 17:22
*
* 聊天服务器
* 多线程
*
*/
public class ChatServer {
public static void main(String[] args) throws IOException {
//1创建ServerSocket
ServerSocket listener=new ServerSocket(9999);
Map<String,Socket> maps= Collections.synchronizedMap(new HashMap<>());
//2监听
System.out.println("聊天服务器已启动....");
try {
while(true) {
Socket socket = listener.accept();
System.out.println("连接成功:"+socket.getInetAddress().getHostAddress());
maps.put(socket.getInetAddress().getHostAddress(), socket);
new ChatThread(socket,maps).start();
}
} catch (IOException e) {
System.out.println("聊天室结束了");
}finally {
listener.close();
}
}
}
线程类
package com.qf;
import java.io.*;
import java.net.Socket;
import java.util.Map;
/*
* wgy 2019/8/12 17:24
*/
public class ChatThread extends Thread {
private Socket socket;
private Map<String,Socket> maps;
public ChatThread(Socket socket,Map<String,Socket> maps) {
this.socket = socket;
this.maps=maps;
}
@Override
public void run() {
BufferedReader br=null;
if(socket!=null){
try {
InputStream is = socket.getInputStream();
br=new BufferedReader(new InputStreamReader(is));
while(true){
String data = br.readLine();
if(data==null){
System.out.println(socket.getInetAddress().getHostAddress()+"退出了");
maps.remove(this.socket.getInetAddress().getHostAddress());
break;
}
System.out.println(socket.getInetAddress().getHostAddress()+"说:"+data);
if(data.equals("baibai")||data.equals("over")||data.equals("886")){
System.out.println(socket.getInetAddress().getHostAddress()+"退出了");
maps.remove(this.socket.getInetAddress().getHostAddress());
break;
}
//转发数据 10.9.21.188:你好
//1截取ip地址
String ip=data.substring(0, data.indexOf(":"));
String pattern = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}";
if(ip!=null){
boolean result = ip.matches(pattern);
if(result){
Socket socket = maps.get(ip);
if(socket!=null){
//2转发
synchronized (socket){
OutputStream os = socket.getOutputStream();
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(os));
String address = this.socket.getInetAddress().getHostAddress();
bw.write(address+"说:"+data.substring(data.indexOf(":")+1));
bw.newLine();
bw.flush();
System.out.println(address+"给"+ip+"转发了数据");
}
}
}
}
}
} catch (Exception e) {
//e.printStackTrace();
//e.printStackTrace();
System.out.println(socket.getInetAddress().getHostAddress()+"异常退出了...");
maps.remove(this.socket.getInetAddress().getHostAddress());
}finally {
try {
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端:
package com.qf;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/*
* wgy 2019/8/12 17:30
*/
public class ChatClient {
public static void main(String[] args) throws Exception{
//1创建socket
Socket socket=new Socket("10.9.21.146", 9999);
//2发送线程
new Thread(new Runnable() {
@Override
public void run() {
BufferedWriter bw=null;
try {
//2 获取输出流
OutputStream os = socket.getOutputStream();
bw=new BufferedWriter(new OutputStreamWriter(os));
//3发送
Scanner input=new Scanner(System.in);
while(true){
String data = input.nextLine();
bw.write(data);
bw.newLine();
bw.flush();
if(data.equals("baibai")||data.equals("over")||data.equals("886")){
break;
}
}
//4关闭
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bw.close();
if(!socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
//3接受数据线程
new Thread(new Runnable() {
@Override
public void run() {
BufferedReader br=null;
try {
InputStream is = socket.getInputStream();
br=new BufferedReader(new InputStreamReader(is));
while (true) {
String data = br.readLine();
System.out.println(data);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
br.close();
if(!socket.isClosed()) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}