网络编程
网络模型
OSI 参考模型
应用层 |
---|
表示层 |
会话层 |
传输层 |
网络层 |
数据联络层 |
物理层 |
TCP/IP 参考模型
应用层 |
---|
传输层 |
网络层 |
主机至网络层 |
网络编程三要素
IP地址
端口号
传输协议
UDP 和TCP
Socket
UDP Socket传输协议
UDP文本传输
import java.net.*;
import java.io.*;
/*
需求:通过udp传输方式,将一段文字数据发送出去。
定义一个发送窗口
思路:
1、建立udpsocket服务。
2、提供数据,并将数据封装到数据包中,
3、通过socket服务的发送功能,将数据包发送出去。
4、关闭资源
*/
public class Udpsend {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//1、创建udp服务,通过DatagramSocket对象。
DatagramSocket ds= new DatagramSocket();
//2、确定数据,并封装成数据包;DatagramPacket(byte[] buf,int length,InetAddress adress, int port)
//byte[] buf="udp ge men lai le".getBytes();//静态录入
//键盘录入
BufferedReader bufr= new BufferedReader(new InputStreamReader(System.in));
String line = null;
//DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10008);
while ((line=bufr.readLine())!=null)
{
if("886".equals(line))break;
byte [] buf = line.getBytes();
DatagramPacket dp= new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10008);
//3、通过socket服务,将已有的数据包发送出去,通过send方法
ds.send(dp);
}
//4、关闭资源。
ds.close();
}
}
/*需求:
定义一个应用程序,用于接收udp协议传输的数据并处理
定义udp的接收端
思路:
1、定义udpsocket服务。通常会监听一个端口,其实就是给这个接收网路应用的程序定义数字标识。
方便于明确哪些数据过来应该应用程序可以处理。
2、定义一个数据包,因为要存储接收到的字节数据。
因为数据包对象中有更多功能可以提取字节数据中的不同的数据信息。
3、通过socket服务的receive方法将收到的数据存入已经定义好的数据包中。
4、通过数据包对象的特有功能,将这些不同的数据取出。打印在控制台上。
*/
class Udpreceive{
public static void main(String[] args) throws Exception{
//1、创建udp socket,建立端点。
DatagramSocket ds= new DatagramSocket(10008);
//循环接收数据
while(true){
//2、定义数据包,用于存储数据。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3、通过服务的receive方法将收到的数据存入数据包中。
ds.receive(dp);
//4、通过数据包的方法获取其中的数据。
String ip=dp.getAddress().getHostAddress();
String data= new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(ip+":"+data+":"+port);
}
//5 关闭资源
//ds.close();
}
}
UDP 聊天程序实例
import java.net.*;
import java.io.*;
/*
需求:编写一个聊天程序。
有收数据的部分和发数据的部分。
这俩个部分需要同时执行
那就需要用到多线程技术。
一个线程控制收, 一个线程控制发。
因为收和发动作是不一致的,所以要定义俩个run方法。
而且这俩个方法要封装到不同的类中。
*/
public class ChatDemo {
/**
* @param args
* @throws SocketException
*/
public static void main(String[] args) throws SocketException {
// TODO Auto-generated method stub
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket = new DatagramSocket(10008);
new Thread(new Send(sendSocket)).start();
new Thread(new Receive(receiveSocket)).start();
}
}
class Send implements Runnable{
private DatagramSocket ds;
public Send(DatagramSocket ds){
this.ds=ds;
}
public void run(){
try{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = bufr.readLine())!=null){
if("886".equals(line))break;
byte [] buf = line.getBytes();
DatagramPacket dp= new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10008);
ds.send(dp);
}
}catch(Exception e){
throw new RuntimeException("发送异常");
}
}
}
class Receive implements Runnable{
private DatagramSocket ds;
public Receive(DatagramSocket ds){
this.ds=ds;
}
public void run(){
try{
while(true){
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
String date = new String (dp.getData(),0,dp.getLength());
System.out.println(ip+":"+date);
}
}catch(Exception e){
throw new RuntimeException("接收失败");
}
}
}
TCP的Socket传输
TCP 网络书传输 实例
/*
tcp传输。
1.tcp分客户端和服务端。
2.客户端对应的对象是Socket,服务端对应的对象是ServerSocket。
*/
import java.io.*;
import java.net.*;
public class TcpDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
/*
客户端
通过查阅socket对象,发现在该对象建立时,就可以去连接指定的主机。
因为tcp是面向连接的,所以在建立socket服务时,
就要有服务端存在,并连接成功,形成通路后,在该通道进行数据传输
需求:给服务端发送一个文本数据。
步骤:
1.创建Socket服务。并指定要连接的主机和端口。
*/
class TcpClient{
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//创建客户端的socket服务,指定目的主机和端口
Socket s= new Socket("127.0.0.1",10008);
//为发送数据,应该获取socket流中的输出流。
OutputStream out= s.getOutputStream();
byte[] buf ="tcp ge mem lai le".getBytes();
out.write(buf);
InputStream in=s.getInputStream();
byte[] bufin = new byte[1024];
int len= in.read(buf);//就收服务端发挥的信息 阻塞式方法。
System.out.println(new String (buf,0,len));
//关闭socket
s.close();
}
}
/*
服务端:
1.建立服务端的socket服务,ServerSocket();
并监听一个端口。
2.获取连接过来的 客户端对象。
通过ServerSocket 的accept方法。没有连接就会等,所以该方法是阻塞方式。
3.客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户对象的读取流来读取数据
并打印在控制台上。
4.关闭服务器。(可选的)
*/
class TcpServer{
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//建立服务端的socket服务,并监听一个端口。
ServerSocket ss= new ServerSocket(10008);
// 通过accept方法获取连接过来的客户端对象。
Socket s = ss.accept();
//获取客户端发送过来的数据,那么要使用客户端对象的读取流来读数据。
InputStream in=s.getInputStream();
byte[] buf=new byte[1024];
int len=in.read(buf);
String data=new String(buf,0,len);
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"------connected!");
System.out.println(data);
OutputStream out=s.getOutputStream();
out.write("哥们收到,你也好!".getBytes());
}
}
TCP传输文本转换功能 实例
import java.io.*;
import java.net.*;
/*
需求:建立一个文本转换服务器。
客户端给服务器发送文本,服务端将文本转化成大写在返回给客户端。
而且客户端可以不断的进行文本的转换。当客户端输入over时,转换结束。
分析:
客户端:既然是操作设备的数据,那么久可以使用IO技术,并按照IO的操作规律来思考。
源:键盘录入。
目的:网络设备,网络输出流。 而且操作的是文本数据,可以选择字符流。
步骤:
1.建立服务。
2.获取键盘录入。
3.将数据发给服务端。
4.去服务端返回的大写数据。
5.结束,关闭资源。
都是文本数据,可以使用字符流进行操作,同时提高效率, 加入缓冲。
*/
public class TransTextDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
class TransClient{
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Socket s = new Socket("127.0.0.1",10008);
//定义一个键盘输入流
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//定义的目的,将数据写入到socket 输出流。发给服务端。
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//使用PrintWriter 要比用BufferedWriter 更方便自动刷新和println方法自动换行
PrintWriter pwOut= 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;
pwOut.println(line);
//bufOut.write(line);
//bufOut.newLine();//必须加入结束标记,证明此行读取结束,防止接收端无限等待。
//bufOut.flush();//必须刷新缓冲区,否则数据发不出去。
String str =bufIn.readLine();
System.out.println(str);
}
bufr.close();
s.close();
}
}
class TransServer{
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(10008);
Socket s = ss.accept();
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"......connected");
// 读取socket 读取流中的数据。
BufferedReader bufIn=
new BufferedReader(new InputStreamReader(s.getInputStream()));
//目的。socket输出流。将大写数据写入到socket输出流,并发送个客户端。
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pwOut = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line=bufIn.readLine())!=null){
pwOut.println(line.toUpperCase());
//bufOut.write(line.toUpperCase());
//bufOut.newLine();
//bufOut.flush();
}
s.close();
ss.close();
}
}
TCP 文件上传 实例
import java.io.*;
import java.net.*;
/*
需求分析:将一文件通过客户端网路传输到服务器,服务器将其保存
*/
public class UploadFile {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
/*
客户端:通过socket的io流将文件传输到服务端
*/
class ClientFile{
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
Socket s= new Socket("127.0.0.1",10008);
//建立一要上传的文件
File uploadfile= new File("C:/ChatDemo.java");
//建立socket字符输出流
PrintWriter pwOut = new PrintWriter(s.getOutputStream(),true);
//建立socket输入流
BufferedReader bufIn =new BufferedReader(new InputStreamReader(s.getInputStream()));
//判断文件是否存在 存在就进行传输数据
if(uploadfile.exists()){
// 建立文件输入流 读取对应的文件数据
BufferedReader bufr= new BufferedReader(new FileReader(uploadfile));
//将文件名传输给服务端
pwOut.println(uploadfile.getName());
//接收服务器传回来的 文件是否存在的信息
System.out.print(bufIn.readLine());
String line= null;
//将接收的文件数据通过socket输出流传输给服务端
while((line=bufr.readLine())!=null){
pwOut.println(line);
}
bufr.close();
//数据传输完必须告诉服务端输出流传输结束。否则服务端进入无限等待状态。
s.shutdownOutput();
}else{
System.out.println("文件不存在!");
}
//读取服务端最后的反馈信息
String str= bufIn.readLine();
System.out.println(str);
s.close();
}
}
class ServerFile{
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
ServerSocket ss= new ServerSocket(10008);
Socket s= ss.accept();
//查询客服端是否连接进来
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"......connected");
//socket输入流
BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
//socket输出流
PrintWriter pwOut= new PrintWriter(s.getOutputStream(),true);
//文件输出流
PrintWriter pw=null;
//读取客服端传过来的文件名
String filename= bufIn.readLine();
//创建文件
File uploadPath=new File("d:/"+filename);
//判断文件是否存在
if(!uploadPath.exists()){
// 建立文件输出流
pw = new PrintWriter(uploadPath);
//返回给客户端保存的文件名
pwOut.println("上传的文件为:"+filename);
String line =null;
while ((line=bufIn.readLine())!=null){
pw.println(line);
}
pw.close();
pwOut.println("上传成功!");
}else{
//如果文件名存在了 返回相关信息
pwOut.println("上传的文件存在!");
pwOut.println("上传失败!");
}
//将服务端延迟10秒,避免服务端在客户端没有完成数据接收就断开连接
new Thread().sleep(10000);
// s.close();
//ss.close();
}
}
TCP图片的并发上传 实例
import java.io.*;
import java.net.*;
import java.util.*;
/*需求:上传图片
/* 客户端
* 1、服务端
* 2、读取客户端已有图片信息
* 3、通过socket 输出流将数据发给服务器
* 4、读取服务端的反馈信息
* */
public class Uploadpic {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
}
}
class PicClient {
public static void main(String[] args) throws Exception{
Scanner sc=new Scanner(System.in);
System.out.println("请输入一个上传图片路径");
String str=sc.nextLine();
List<String> filepath =new ArrayList<String>();
filepath.add(str);
if(filepath.size()!=1){
System.out.println("请选择一个图片路径!");
return ;
}
File file = new File(filepath.get(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("127.0.0.1",10008);
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[] bufin= new byte[1024];
int num = in.read(bufin);
System.out.println(new String(bufin,0,num));
fis.close();
s.close();
}
}
//创建一个图片上传的线程类,实现客户端并发上传文件;
class PicThread implements Runnable{
private Socket s=null;
PicThread(Socket s){
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"......connected!");
int Count=1;
try {
File file=new File("d:/"+ip+".jpg");
while(file.exists()){
file=new File("D:/"+ip+"("+(Count++)+").jpg");
}
InputStream in = s.getInputStream();
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len =0;
while ((len=in.read(buf))!=-1){
fos.write(buf,0,len);
}
OutputStream out = s.getOutputStream();
out.write("上传成功!".getBytes());
fos.close();
s.close();
} catch (Exception e) {
throw new RuntimeException("上传失败!");
}
}
}
class PicServer{
public static void main(String[] args) throws IOException{
ServerSocket ss = new ServerSocket(10008);
while(true){
Socket s=ss.accept();
new Thread(new PicThread(s)).start();
}
// ss.close();
}
}
URL对象的使用
url对象常用方法:
import java.net.*;
/* URL 对象常用方法
* getFile()
获得此 URL 的文件名。
String getHost()
获得此 URL 的主机名(如果适用)。
String getPath()
获得此 URL 的路径部分。
int getPort()
获得此 URL 的端口号。
String getProtocol()
获得此 URL 的协议名称。
String getQuery()
获得此 URL 的查询部分。
*/
public class URLDemo {
public static void main(String[] args) throws MalformedURLException {
// TODO Auto-generated method stub
URL url = new URL("http://localhost:8080/web/demo.html?a=aa%&b=11");
System.out.println("getProtocol():"+url.getProtocol());
System.out.println("getHost():"+url.getHost());
System.out.println("getPort():"+url.getPort());
System.out.println("getPath():"+url.getPath());
System.out.println("getFile():"+url.getFile());
System.out.println("getQuery():"+url.getQuery());
//web访问时一般不带端口号,端口号默认为-1,一帮将其设置为80
int port = url.getPort();
if(port==-1)
port=80;
}
}
URLConnection 对象访问服务器实例
import java.io.*;
import java.net.*;
public class URLConnectionDemo {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
// 获取的是tomcat服务器下的web项目里的demo.html文件。 必须先启动tomcat服务器
URL url = new URL("http://127.0.0.1:8080/web/demo.html");
//使用URL对象建立的是应用层的连接。返回的是去掉HTTP所识别的信息(也就是消息头部的信息),只返回文件内容信息。
URLConnection conn= url.openConnection();
InputStream in=conn.getInputStream();
byte[] buf=new byte[1024];
int len= in.read(buf);
System.out.println(new String(buf,0,len));
in.close();
}
}
附加知识:域名解析
当我们访问一个网络地址时:如http://www.sohu.com/ 浏览器首先在本地的地址映射文件配置中找是否有相关的 www.sohu.com 这个名称的地址。如果找不到就会在网络的
DNS(域名解析器)中去请求数据信息,如果域名解析器有这个名称的注册就返回这个名称的IP地址给浏览器,然后浏览器就会根据IP地址去访问IP地址的服务器。
当我们访问http://localhost:8080/这个地址是他也是先访问我们本机的映射配置文件去找 配置文件就在C:\Windows\System32\drivers\etc下的 hosts文件 内容如下:
# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
# localhost name resolution is handled within DNS itself.
# 127.0.0.1 localhost
# ::1 localhost
127.0.0.1 localhost