——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
注:视频来源,毕向东老师的 JAVA 基础视频。
一、网络编程的基本组成部分
1、网络模型
–>OSI参考模型
–>TCP/IP参考模型
2、网络通讯要素
2.1 IP地址
–>–>网络中设备的标识
–>–>不易记忆,可用主机名
–>–>本地回环地址:127.0.0.1主机名:localhost
2.2 端口号
–>–>用于标识进程的逻辑地址,不同进程的标识
–>–>有效端口:0~65535,其中0~1024为系统使用或保留端口。
2.3 传输协议
–>–>通讯的规则
–>–>常见协议:TCP,UDP
数据通信的原理:就是数据传输的过程。
参考模型图示:
不同主机间的通信,数据包的封包与拆包:
二、TCP和UDP的特点
1) UDP:
1、将数据及源和目的封装成数据包中,不需要建立连接。
2、每个数据报的大小限制在64k。
3、因为是无连接,是不可靠的协议。
4、不需要建立连接,传输速度快。
2) TCP:
1、建立连接,形成传输数据的通道。
2、在连接中进行大数据量传输。
3、通过三次握手完成连接,是可靠协议。
4、必须建立连接,效率会稍低。
3) 认识 Socekt :(翻译为:插座)
Socket 就是为网络服务提供的一种机制。
1、通信的两端都是 Socket 。
2、网络通信其实就是 Socket 间的通信。
3、数据在两个 Socket 间通过 IO 传输
三、UDP 传输:(特有传输方式)
DatagramSocket:此类表示用来发送和接受数据报包的套接字。特点:既能发送,又能接收。
DatagramPacket:此类表示数据报包。数据报包用来实现无连接包投递服务。
3.1、UDP 的传输步骤:
1、建立发送端,接收端。
2、建立数据包。
3、调用 Socket 的发送接收方法。
4、关闭 Socket 。
总结:发送端与接收端是两个独立运行的程序。
练习:
package fuxi3;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* 需求:利用Udp传输建立一个聊天工具。
*@author XiaLei
*/
public class Day23Test1 {
public static void main(String[] args) throws SocketException {
//创建服务。通过DatagramSocket对象
DatagramSocket send = new DatagramSocket();
//创建udp Socket服务,建立端点
DatagramSocket rece = new DatagramSocket(10000);
//建立两个线程,分别发送和接受数据。
new Thread(new Send(send)).start();
new Thread(new Rece(rece)).start();
}
}
class Send implements Runnable{
private DatagramSocket ds;
Send(DatagramSocket ds){
this.ds = ds;
}
public void run() {
BufferedReader bufr = null;
try{
bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = bufr.readLine())!=null){
//确定数据,并封装成数据包。DatagramPacket(byte[] buf, int length, InetAddress address, int port)
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10000);
ds.send(dp);//通过Socket服务,将已有的数据包发送出去。通过send方法。
}
}
catch(Exception e){
throw new RuntimeException("发送失败");
}
}
}
class Rece implements Runnable{
private DatagramSocket ds;
Rece(DatagramSocket ds){
this.ds = ds;
}
public void run() {
try{
//一直处于接收状态
while(true){
//定义数据包。用于存储数据
byte[] buf = new byte[1024];
//通过Socket服务的receive方法将接收到的数据存入数据包中
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);//阻塞式方法
//通过数据包的方法获取其中的数据
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"::"+data);
}
}
catch(Exception e){
throw new RuntimeException("接受失败");
}
}
}
四、TCP 传输:Socket 和 ServetSocket
TCP 传输的基本步骤:
1、建立客户端和服务器端
2、建立连接后通过 Socket 中的 IO 流进行数据传输
3、关闭 Socket
总结:同样,客户端和服务器端是两个独立的应用程序。
4.1、演示 tcp 传输。
1、tcp 分客户端和服务器端。
2、客户端对应的对象是 Socket。
3、服务器端对应的对象是 ServetSocekt。
客户端code:
package fuxi3;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* 需求:建立一个文本转换服务器。
* 客户端不断发送文本数据,服务端接受数据,并将数据转换为大写返回。
* 当客户端输入“over”,转换结束。
*@author XiaLei
*/
public class TcpClient {
public static void main(String[] args) throws Exception {
//创建客户端的 Socket 服务,指定目的主机和端口
Socket s = new Socket("127.0.0.1",10001);
//建立缓冲区,将键盘录入存入缓冲区
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//创建打印流对象加载进Socket输出流,并自动刷新
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//创建读取缓冲区与Socket输出流关联
BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line = bufr.readLine())!=null){
if("over".equals(line))
break;
out.println(line);//将键盘录入信息载入Socket输出流
String len = bufin.readLine();//读取Socket输入流信息并打印到控制台
System.out.println("client"+len);
}
//关流
bufr.close();
//关客户端
s.close();
}
}
服务端code:
package fuxi3;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
*@author XiaLei
*/
public class TcpSever {
public static void main(String[] args) throws Exception {
//建立服务端的 Socket 服务。并监听一个端口。
ServerSocket ss = new ServerSocket(10001);
//通过 accept 方法获取连接过来的客户端对象。
Socket s = ss.accept();
//获取ip
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".....connect");
//获取客户端发送过来的数据,那么要使用客户端的读取流方法
BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));
//将读取的字符串改为大写发送回客户端
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line = bufin.readLine())!=null){
System.out.println("server"+line);
out.println(line.toUpperCase());
}
//关闭客户端,必须关闭客户端
s.close();
ss.close();
}
}
4.2、练习:利用Tcp传输,上传图片
客户端code:
package fuxi3;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
/**
*
*@author XiaLei
*/
public class TcpClientPic {
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.1.1.0",10002);
OutputStream out = s.getOutputStream();
FileInputStream fis = new FileInputStream("D:\\1.jpg");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf))!=-1){
out.write(buf,0,len);
}
s.shutdownOutput();//设定结束标记,因为服务器端一直在读数据,它不知道什么时候停止。
BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = bufr.readLine();
System.out.println(line);
fis.close();
s.close();
}
}
服务端code:
package fuxi3;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
*@author XiaLei
*/
public class TcpServerPic {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10002);
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress()+"....connect");
InputStream in = s.getInputStream();
FileOutputStream fos = new FileOutputStream("D:\\2.jpg");
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf))!=-1){
fos.write(buf,0,len);
}
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bufw.write("服务器接受成功");
bufw.flush();//建立缓冲区一定要记得刷新,不刷新客户端接受不到数据,打印null。
fos.close();
s.close();
ss.close();
}
}
第五部分 综合应用
1、多并发上传图片文件
建立PicClient and PicServer
1.1 客户端
需求:上传图片
实现的基本步骤:
1、建立服务端点。
2、读取客户端已有的图片数据。
3、通过 Socket 输出流将数据发给服务端。
4、读取服务端反馈信息。
5、关闭。
code:
package 并发传图片;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
*
*@author lenovo
*/
public class Client {
public static void main(String[] args) throws Exception{
if(args.length!=1){
System.out.println("请选择一个jpg格式的图片文件");
}
File file = new File(args[0]);
if(!(file.exists() && file.isFile())){
System.out.println("该文件存在问题,要不不存在,要么不是文件。");
return;
}
if(!(file.getName().endsWith(".jpg"))){
System.out.println("该文件不是 jpg 格式图片,请重新选择。");
return;
}
//防止视频文件,通过修改了后缀名存入,限制上传图片的大小
if(file.length()>1024*1024*5){
System.out.println("文件太大,限制上传。");
return;
}
Socket s=new Socket("192.168.0.100",10010);
FileInputStream fis=new FileInputStream(file);
OutputStream out=s.getOutputStream();
byte[] buf=new byte[1024];
int len=0;
while ((len=fis.read(buf))!=-1){
out.write(buf,0,len);
}
//创建结束标记
s.shutdownOutput();
//获取服务器端发回来的接收进度信息
InputStream in=s.getInputStream();
byte[] b=new byte[100];
int x=0;
while ((x=in.read(b))!=-1){
System.out.println(new String(b,0,x));
}
fis.close();
s.close();
}
}
1.2 服务器端
1、服务端服务,并监听窗口
2、获取客户端对象,并获取客户ip
3、读取客户端输入流数据
4、写入文件
5、用客户端输出流反馈信息
6、关流
注意:
为了让多个客户端同时并发访问服务端。服务端最好是将每个客户端请求封装到一个单独的线程中,这样就可以同时处理多个客户端的请求了。
那么如何定义线程呢?只要明确了每一个客户端要在服务端执行的代码即可,将该代码存入 run 方法中。
服务器端code:
package 并发传图片;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
/**
*
*@author lenovo
*/
class PicServer implements Runnable {
private Socket s;
PicServer(Socket s){
this.s=s;
}
public void run(){
int count=1;
String ip=s.getInetAddress().getHostAddress();
try {
System.out.println(ip+"...connect");
//通过客户端的读取流读取数据
InputStream ins=s.getInputStream();
File file=new File(ip+"("+count+")"+".jpg");
//判断文件是否存在
if (file.exists())
file=new File(ip+"("+(count++)+")"+".jpg");
//将数据写入到指定文件中
FileOutputStream fos=new FileOutputStream(file);
byte[] buf=new byte[1024];
int len=0;
while ((len=ins.read(buf))!=-1){
fos.write(buf,0,len);
fos.close();
s.close();
}
//将收到图片数据的信息返回给客户端
OutputStream outs=s.getOutputStream();
outs.write("上传成功".getBytes());
}
catch(Exception e){
throw new RuntimeException("上传失败");
}
}
}
class Server {
public static void main(String[] args)throws Exception{
ServerSocket ss=new ServerSocket(10010);
while (true){
//获取客户端对象
Socket s=ss.accept();
//客户端执行线程
new Thread(new PicServer(s)).start();
}
}
}
2、客户端并发登录
客户端通过键盘录入用户名,服务端对这个用户名进行校验。
如果该用户存在,在服务端显示xxx,已登陆;并在客户端显示xxx,欢迎光临。
如果用户不存在,在服务端显示xxx,尝试登陆;并在客户端显示xxx,该用户不存在。
最多就登录三次。
客户端code:
package 客户端并发登录;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
*
*@author lenovo
*/
public class Client {
public static void main(String[] args) throws Exception{
Socket s=new Socket("127.0.0.1",10100);
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
PrintWriter out=new PrintWriter(s.getOutputStream(),true);
BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
for (int x=0;x<3;x++){
String line=bufr.readLine();//读取键盘录入
if(line==null)
break;
out.println(line);
String info=bufIn.readLine();
if (info.contains("欢迎"))//如果登录成功,就跳出循环
break;
}
bufr.close();
s.close();
}
}
服务器端code:
package 客户端并发登录;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
*@author lenovo
*/
public class Server {
public static void main(String[] args) throws Exception{
ServerSocket ss=new ServerSocket(10100);
while (true){
Socket s=ss.accept();
//客户端执行线程
new Thread(new DenLu(s)).start();
}
}
}
class DenLu implements Runnable{
private Socket s;
DenLu(Socket s){
this.s=s;
}
public void run (){
System.out.println(s.getInetAddress().getHostAddress()+"...connected");
try{
for (int x=0;x<3;x++){
//读取数据库中的数据,这里用文件来表示数据库
BufferedReader bufr=new BufferedReader(new FileReader("D:\\username.txt"));
//通过客户端的读取流读取数据
BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out=new PrintWriter(s.getOutputStream(),true);
String name=bufIn.readLine();
if (name==null)
break;
String line=null;
boolean flag=false;//设置标记
while((line=bufr.readLine())!=null){
if (line.equals(name)){
flag=true;
break;
}
}
if(flag){
System.out.println(name+"用户已登录");
out.println(name+"欢迎登录");
break;
}
else{
System.out.println(name+"尝试登录");
out.println(name+"登录失败");
}
bufr.close();
s.close();
}
}
catch(Exception e){
throw new RuntimeException(s.getInetAddress().getHostAddress()+"校验失败");
}
}
}
第六部分 DNS 域名解析服务器
图解:
1、地址栏访问主机的时候,首先是先访问本机系统内的 C–>System32–>
Drivers–>hosts文件
2、常见问题:禁止软件更新的方法(通常软件更新,会带来收费的问题)
到 hosts 文件夹中,把软件更新的地址,如:www.myeclipse.com 加入到列表中。
例子:127.0.0.1 www.myeclipse.com
3、那么,当软件要更新访问的地址就会变成本机地址,所以就会访问失败,所以软件更新失败 。就好比杀毒软件。发现一些垃圾网站,就往 hosts 文件中加入这个网站来屏蔽。