一.网络基础知识
1.OSI(Open System Interconnection)七层模型和TCP/IP协议
2.七层模型与协议之间的对应关系
3.网络编程三要素:
1)IP地址:每台电脑都有唯一的标识:ip
常用的IP A类:1网络+3主机 可连接主机个数 2563 通常大学里面或一些政府部门(1-127)
B类:2网络+2主机 可连接主机个数 2562 (128-191)
C类:3网络+1主机 可连接主机个数 256(192-223)
127.0.0.1:回环地址(也可以表示本机地址)
xxx.xxx.xxx.255广播地址
ping ip地址:查看当前本机与这台PC机器的网络通信
2)端口:两台PC通信,除了知道IP地址,还要知道端口号
有效端口号:0-65535
保留端口好:0-1024
3)协议:TCP协议和UDP协议区别:
TCP(传输控制协议):
建立连接通道(通道内的流使用的是最基本的字节流)
可靠协议
服务器要等待客户端连接,执行效率低
使用TCP协议可以发送大量的数据,文件大小无限制
UDP(用户数据报协议):
无需建立连接通道(数据包的形式)
不可靠协议
执行效率高
UDP发送数据有限制
4.如果一个类没有构造方法(三种情况)
1)这个类里面的成员方法都是静态(Math、Arrays、Collections)
2)单例模式:在内存始终只有一个对象,将构造方法私有化,在成员变量创建该类的实例(并且该实例被static修饰)提供该类公共方法可以通过外界调用
3)该类中会有某些静态成员方法的返回值是该类本身(InetAddress类:表示互联网协议(IP)地址)
5.InetAddress类:表示互联网协议(IP)地址
常用方法:
1)String getHostAddress()
返回 IP 地址字符串(以文本表现形式)
2)byte[]
getAddress()返回此
InetAddress
对象的原始 IP 地址
6.Socket编程
包括两种:TCP和UDP编程,两端必须有socket对象,还必须有IP地址和端口号
二.UDP编程
发送端(Send):
1.创建UDP协议发送端的Socket对象(DatagramSocket()):用来发送和接收数据包的套接字
2.创建数据包(通过这个数据包将数据发送到接收端):public DatagramPacket(byte[] buf, int length ,InetAddress address,int port)
3.调用UDP协议发送端发送的方法
4.关闭资源
接收端(Receive):
1.创建接收端的Socket对象(DatagramSocket())
2.创建一个数据包接收发送端发来的数据包
3.接收数据,调用DateSocket类中的接收方法:阻塞式,等待发送端发送数据,没有数据就一直等待
4.解析数据包里面的实际数据,显示在控制台上
5.关闭资源
package udp2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//import java.net.SocketException;
//import java.net.UnknownHostException;
/**发送端:
* 1.创建UDP协议发送端的socket对象(DatagramSocket())
* 2.创建数据包:通过数据包将数据发送到接收端:public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
* 3.调用UDP协议发送方法
* 4.关闭资源
*/
public class SendDemo {
public static void main(String[] args) throws Exception {
//1.创建UDP协议发送端的socket对象(DatagramSocket())
DatagramSocket ds = new DatagramSocket();
//2.创建数据包public DatagramPacket(byte[] buf,int length,InetAddress address,int port)
byte[] bys = "hello udp".getBytes();
DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.1.131"),666);
//3.调用UDP协议发送方法
ds.send(dp);
//4.关闭资源
ds.close();
}
}
package udp2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
//import java.net.SocketException;
/**接收端:
* 1.创建接收端的Socket对象
* 2.创建一个数据包接收发送端发来的数据报包(接收容器)
* 3.接收数据,调用DateSocket类中的接收方法来接收数据包
* 4.解析数据包里面的实际数据,显示在控制台上
* 5.关闭资源
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//1.创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(666);
//2.创建一个数据包接收发送端发来的数据报包(接收容器)
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys,bys.length);
//3.接收数据
ds.receive(dp);
//4.解析数据并显示在控制台上
String ip = dp.getAddress().getHostAddress();
//获取缓冲区中的实际数据
byte[] dataBys = dp.getData();
//获取实际缓冲区的数据的长度
int len = dp.getLength();
String s = new String(dataBys,0,len);
//输出到控制台
System.out.println(ip+"传输的数据是:"+s);
//关闭资源
ds.close();
}
}
键盘录入的两种方式:
1.Scanner类
2.BufferedReader类(字符缓冲流)特有功能 readLine():一次读取一行
BufferedReader br = new BufferedReader(new InputStreamReader(system.in));
对以上的发送端进行优化,发送端的数据来源是不停的键盘录入
package udp3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//import java.net.SocketException;
public class SendDemo {
public static void main(String[] args) throws IOException {
//1.创建UDP协议发送端的socket对象(DatagramSocket())
DatagramSocket ds = new DatagramSocket();
//2.创建数据包public DatagramPacket(byte[] buf,int length,InetAddress address,int port
//字符缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//一次读取一行
String line = "null";
while((line=br.readLine())!=null){
//发送端不停发送的结束条件
if("886".equals(line)){
break;
}
//创建数据包
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.1.131"),888);
//3.调用UDP协议发送方法
ds.send(dp);
}
//4.释放资源
ds.close();
}
}
package udp3;
//import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
//import java.net.InetAddress;
import java.net.SocketException;
public class ReceiveDemo {
public static void main(String[] args) throws SocketException,Exception {
// 1.创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(888);
while(true){
//2.创建接收容器
//创建字符缓冲区
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
//3.接收数据
ds.receive(dp);
//4.解析数据
//获取发送端的IP地址
String ip = dp.getAddress().getHostAddress();
//获取实际缓冲区的数据
byte[] dataBys = dp.getData();
//实际缓冲区的长度
int len = dp.getLength();
String s = new String(dataBys, 0, len);
//显示到控制台上
System.out.println(ip+"发送了数据:"+s);
//5.释放资源:由于模范一直发送的数据,所以不用释放
//ds.close();
}
}
}
接收端和发送端是在两个窗口中显示的,如何让这两个接收端和发送端处于一个窗口下(main中)
package udprunnable;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ChatRoom {
/**
* 需求:接收端和发送端是在两个窗口中显示的,如何让这两个接收端和发送端处于一个窗口下(main中)
* (使用多线程第二种方式:Runable接口的方式实现 发送端和接收端处于一个主线程中)
*/
public static void main(String[] args) {
//创建发送端和接收端的Socket对象
try {
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket = new DatagramSocket(888);
//创建资源对象
SendThread st = new SendThread(sendSocket);
ReceiveThread rt = new ReceiveThread(receiveSocket);
//创建线程类对象
Thread t1 = new Thread(st);
Thread t2 = new Thread(rt);
//启动线程
t1.start();
t2.start();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package udprunnable;
import java.io.BufferedReader;
//import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//import java.net.UnknownHostException;
public class SendThread implements Runnable{
private DatagramSocket ds;
public SendThread(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
try {
//创建发送端Socket对象
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//一行读取一个数据
String line = null;
while((line=br.readLine())!=null){
//结束条件
if("886".equals(line)){
break;
}
//创建数据包
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.1.131"),888);
//发送数据
ds.send(dp);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//释放资源
if(ds!=null){
ds.close();
}
}
}
}
package udprunnable;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveThread implements Runnable{
private DatagramSocket ds;
public ReceiveThread(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
try {
while(true){
//创建接收容器
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
//接收数据
ds.receive(dp);
//解析数据
String ip = dp.getAddress().getHostAddress();
//获取实际缓冲区的数据
byte[] dateBys = dp.getData();
//实际实际缓冲区数据的长度
int len = dp.getLength();
String s = new String(dateBys,0,len);
System.out.println(ip+"发送的数据是"+s);
}
//接收要不停接收数据,不需要关闭
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
三.TCP编程
客户端步骤:
1.创建客户端的Socket对象(Socket)
2.获取通道内的流(输出流)
3.使用输出流写数据
4.关闭客户端的Socket对象
服务器端步骤:
1.创建服务器端的Socket对象(ServerSocket)
2.服务器端需要监听客户端的连接
public Socket accept() throws IOException
3.获取通道内的输入流
4.将客户端发送的数据显示在控制台上
package tcp;
import java.io.IOException;
//import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo {
/**客户端
* 1.创建客户端的Socket对象
* 2.获取通道内的输出流
* 3.使用输出流写数据
* 4.关闭客户端的流
* @throws IOException
* @throws UnknownHostException
*
*
*/
public static void main(String[] args) throws UnknownHostException, IOException {
//1.创建客户端的Socket对象
Socket s = new Socket("192.168.10.136",8888);
//2.获取通道内的输出流
OutputStream out = s.getOutputStream();
//3.使用输出流写数据
out.write("hello TCP".getBytes());
//4.关闭客户端的流
s.close();
}
}
package tcp;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
/**服务器端:
* 1.创建服务器端的socket对象
* 2.监听服务器端的连接
* 3.获取通道内的输入流
* 4.将客户端的数据显示在控制台上
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 1.创建服务器端的socket对象
ServerSocket ss = new ServerSocket(8888);
//2.监听服务器端的连接
Socket s = ss.accept();
//3.获取通道内的输入流
InputStream in = s.getInputStream();
//4.将客户端的数据显示在控制台上
byte[] bys = new byte[1024];
int len = in.read(bys);
String str = new String (bys,0,len);
//获取主机的IP地址
InetAddress address = s.getInetAddress();
String ip = address.getHostAddress();
System.out.println(ip+"发送的数据是:"+str);
//关闭服务器
s.close();//关闭掉侦听的客户端所在的通道内的对象(Socket)
ss.close();
}
}
改进版1:互相加入反馈
package tcp2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class CilentDemo {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException, IOException {
//1.创建客户端的Socket对象
Socket s = new Socket("192.168.1.131",888);
//2.获取通道内的输出流
OutputStream out = s.getOutputStream();
//3.使用输出流写数据
out.write("hello TCP".getBytes());
//获取到服务器反馈的数据
//获取通道内的输入流数据
InputStream in = s.getInputStream();
byte[] bys = new byte[1024];
int len = in.read(bys);//读取服务器端发送来的实际字节数(阻塞式)
//显示服务器端反馈的数据
String client = new String(bys,0,len);
System.out.println("client:"+client);
//4.关闭客户端的流
s.close();
}
}
package tcp2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 1.创建服务器端的socket对象
ServerSocket ss = new ServerSocket(888);
System.out.println("正在等待客户端连接...");
//2.监听服务器端的连接
Socket s = ss.accept();//阻塞式方法结束
System.out.println("该客户端已连接...");
//3.获取通道内的输入流
InputStream in = s.getInputStream();
//4.将客户端的数据显示在控制台上
byte[] bys = new byte[1024];
int len = in.read(bys);
String server = new String (bys,0,len);
//输出客户端发送的数据
System.out.println("server"+server);
//服务器反馈
//获取通道内的输出流
OutputStream out = s.getOutputStream();
out.write("已收到来自客户端的消息".getBytes());
//关闭资源
s.close();//关闭掉侦听的客户端所在的通道内的对象(Socket)
ss.close();
}
}
改进版2:客户端键盘录入数据,服务器端将数据显示在控制台上
注意通道内的流是字节流,所以要封通道内的流
package tcp3;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo {
/**
* 客户端键盘录入消息
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket s = new Socket("192.168.1.131",888);
//键盘录入数据(IO流的形式)
//字符流封装数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//String str = br.readLine();
//封装通道内的流(要发送的数据)
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//一次读取一行数据
String line = null;
while((line=br.readLine())!=null){
//键盘录入一行数据,就把一行数据封装到通道内的流中
//自定义结束条件
if("over".equals(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
s.close();
}
}
package tcp3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 1.创建服务器端的socket对象
ServerSocket ss = new ServerSocket(888);
System.out.println("正在等待客户端连接...");
//2.监听服务器端的连接
Socket s = ss.accept();//阻塞式方法结束
System.out.println("该客户端已连接...");
//3.封装服务器通道内的流
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//一次读取一行数据
String line = null;
while((line=br.readLine())!=null){
//输出数据
System.out.println(line);
}
s.close();
ss.close();
}
}
改进版3
客户端键盘录入,服务器端输出一个文件
客户端同版本2,以下是服务器端
package tcp4;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 1.创建服务器端的socket对象
ServerSocket ss = new ServerSocket(888);
System.out.println("正在等待客户端连接...");
//2.监听服务器端的连接
Socket s = ss.accept();//阻塞式方法结束
System.out.println("该客户端已连接...");
//3.封装服务器通道内的流
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//4.服务器端输出一个文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
//服务器端通道内的流读取客户端的发送数据
String line = null;
while((line=br.readLine())!=null){
//写入到文本中
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
bw.close();
ss.close();
}
}
改进版4 客户端文本文件,服务器端显示在控制台上
服务器端同版本2,以下是客户端
package tcp5;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo {
/**
* 客户端录入文本文件
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket s = new Socket("192.168.1.131",888);
//读取文本文件的内容
//字符流封装数据
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//String str = br.readLine();
//封装通道内的流(要发送的数据)
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//一次读取一行数据
String line = null;
while((line=br.readLine())!=null){
//键盘录入一行数据,就把一行数据封装到通道内的流中
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
br.close();
s.close();
}
}
改进版5 客户端是文本文件,服务器端将文本文件的内容复制到一个新的文本文件中
客户端同版本4,服务器端如下:
package tcp6;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// 1.创建服务器端的socket对象
ServerSocket ss = new ServerSocket(888);
System.out.println("正在等待客户端连接...");
//2.监听服务器端的连接
Socket s = ss.accept();//阻塞式方法结束
System.out.println("该客户端已连接...");
//3.封装服务器通道内的流
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//将通道流中的数据写到文本文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("newB.txt"));
//一次读取一行数据
String line = null;
while((line=br.readLine())!=null){
//输出数据
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
s.close();
bw.close();
ss.close();
}
}
改进版本6 客户端是图片文件,服务器端输出一个图片文件,并且给出反馈
图片:以字节缓冲流的方式读写(BufferedInputStream、BufferedOutputStream)
package tcpimg;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/**
*客户端的图片文件,服务器端输出一个图片文件并且给出反馈
*2017年12月13日
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket s = new Socket("192.168.1.131",888);
//先封装文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("山里.jpg")) ;
//获取通道内的流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
//读取图片信息,一次读取一个字节数组
byte[] bys = new byte[1024];
int len = 0;
while((len = bis.read(bys))!=-1){
//读取字节到输出流中
bos.write(bys,0,len);
bos.flush();
}
//客户端需要给服务器提供一个终止,告诉服务器端我没有数据了
s.shutdownOutput();
//客户端读取服务器端的反馈
InputStream is = s.getInputStream();
byte[] bys2 = new byte[1024];
int len2 = is.read(bys2);
System.out.println(new String(bys2,0,len2));
//关闭资源
bis.close();
s.close();
}
}
package tcpimg;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器端Socket对象
ServerSocket ss = new ServerSocket(888);
//监听客户端连接
Socket s = ss.accept();
//封装通道内的流
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
//输出图片文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("山理.jpg"));
//一次读取一个字节数组
byte[] bys = new byte[1024];
int len = 0;
while((len=bis.read(bys))!=-1){
bos.write(bys, 0, len);
bos.flush();
}
//服务器端给客户端反馈
OutputStream out = s.getOutputStream();
out.write("文件上传成功".getBytes());
//释放资源
bos.close();
s.close();
ss.close();
}
}