您的位置:首页 > 理论基础 > 计算机网络

黑马程序员—【Java基础篇】之网络编程

2015-04-28 15:28 267 查看
------- android培训java培训、期待与您交流!
---------

伙伴们,这篇分享网络编程,开始,跑,哈哈。

一、网络编程

1、网络模型

/**
(1)OSI参考模型
(2)TCP/IP参考模型
*/


参考模型图:



2、网络通讯要素

网络通讯要素主要包括:IP、端口号和传输协议。

/**
(1)IP地址:InetAddress
a、网络中设备的标识
b、不易记忆,可用主机名
c、本地回环地址:127.0.0.1  主机名:localhost
(2)端口号
a、用于标识进程的逻辑地址,不同进程的标识
b、有效端口:0~65535,其中0~1024系统使用或保留端口。
(3)传输协议
a、通讯的规则
b、常见协议:TCP,UDP
*/

二、传输协议

1、概述

/**
传输协议
a、通讯的规则
b、常见协议:TCP,UDP
*/

2、Socket

<pre name="code" class="java">/**
1、定义
Socket就是为网络服务提供的一种机制。通信的两端都有Socket。
2、特点
a、网络通信其实就是Socket间的通信。
b、数据在两个Socket间通过IO传输。
*/



3、TCP和UDP

<span style="font-size:14px;">/**
(1)UDP
a、将数据及源和目的封装成数据包中,不需要建立连接。
b、每个数据报的大小在限制在64k内因无连接,是不可靠协议
c、不需要建立连接,速度快
(2)TCP
a、建立连接,形成传输数据的通道。
b、在连接中进行大数据量传输
c、通过三次握手完成连接,是可靠协议
d、必须建立连接,效率会稍低
*/</span>

UDP传输协议示例:


/**
需求:通过udp传输方式,将一段文字数据发送出去。,
定义一个udp发送端。
思路:
(1)建立updsocket服务。
(2)提供数据,并将数据封装到数据包中。
(3)通过socket服务的发送功能,将数据包发出去。
(4)关闭资源。
*/
class  UdpSend
{
public static void main(String[] args) throws Exception
{
//1,创建udp服务。通过DatagramSocket对象。
DatagramSocket ds = new DatagramSocket(8888);

//2,确定数据,并封装成数据包。DatagramPacket(byte[] buf, int length, InetAddress address, int port)

byte[] buf = "udp ge men lai le ".getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),10000);

//3,通过socket服务,将已有的数据包发送出去。通过send方法。
ds.send(dp);
//4,关闭资源。
ds.close();
}
}
/**
需求:定义一个应用程序,用于接收udp协议传输的数据并处理的,定义udp的接收端。
思路:
(1)定义udpsocket服务。
通常会监听一个端口。其实就是给这个接收网络应用程序定义数字标识,方便于明确哪些数据过来该应用程序可以处理。
(2)定义一个数据包,因为要存储接收到的字节数据。
因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
(3)通过socket服务的receive方法将收到的数据存入已定义好的数据包中。
(4)通过数据包对象的特有功能。将这些不同的数据取出。打印在控制台上。
(5)关闭资源。
*/

class  UdpRece
{
public static void main(String[] args) throws Exception
{
//1,创建udp socket,建立端点。
DatagramSocket ds = new DatagramSocket(10000);
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();

}
}
/**
总结:
(1)DatagramSocket与DatagramPacket;
(2)建立发送端,接收端;
(3)建立数据包;
(4)调用Socket的发送接收方法。
(5)关闭Socket。
发送端与接收端是两个独立的运行程序。
*/<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">    </span>


TCP传输协议示例:

/**
分析:
(1)Socket和ServerSocket
(2)建立客户端和服务器端
(3)建立连接后,通过Socket中的IO流进行数据的传输
(4)关闭socket
注意事项:客户端与服务器端是两个独立的应用程序。
/**
客户端,
通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机。因为tcp是面向连接的。所以在建立socket服务时,就要有服务端存在,并连接成功。形成通路后,在该通道进行数据的传输。
需求:给服务端发送给一个文本数据。
步骤:
(1)创建Socket服务。并指定要连接的主机和端口。
*/
import java.io.*;
import java.net.*;
class  TcpClient
{
public static void main(String[] args) throws Exception
{
//创建客户端的socket服务。指定目的主机和端口
Socket s = new Socket("192.168.1.254",10003);

//为了发送数据,应该获取socket流中的输出流。
OutputStream out = s.getOutputStream();

out.write("tcp ge men lai le ".getBytes());

s.close();
}
}
/**
需求:定义端点接收数据并打印在控制台上。

服务端:
(1)建立服务端的socket服务。ServerSocket();
并监听一个端口。
(2)获取连接过来的客户端对象。
通过ServerSokcet的 accept方法。没有连接就会等,所以这个方法阻塞式的。
(3)客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据。
并打印在控制台。

(4)关闭服务端。(可选)
*/
class  TcpServer
{
public static void main(String[] args) throws Exception
{
//建立服务端socket服务。并监听一个端口。
ServerSocket ss = new ServerSocket(10003);

//通过accept方法获取连接过来的客户端对象。
while(true)
{
Socket s = ss.accept();

String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".....connected");

//获取客户端发送过来的数据,那么要使用客户端对象的读取流来读取数据。
InputStream in = s.getInputStream();

byte[] buf = new byte[1024];
int len = in.read(buf);

System.out.println(new String(buf,0,len));

s.close();//关闭客户端.
}
//ss.close();
}
}
*/<span style="font-size:14px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">   </span>

UDP传输应用练习:

/**
需求:编写一个聊天程序,有收数据的部分和发数据的部分,这两部分需要同时执行。
分析:那就需要用到多线程技术,一个线程控制收,一个线程控制发。
因为收和发动作是不一致的,所以要定义两个run方法,而且这两个方法要封装到不同的类中。

*/
import java.io.*;
import java.net.*;
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)
{

byte[] buf = line.getBytes();

DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10002);

ds.send(dp);

if("886".equals(line))
break;
}
}
catch (Exception e)
{
throw new RuntimeException("发送端失败");
}
}
}

class Rece implements Runnable
{

private DatagramSocket ds;
public Rece(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 data = new String(dp.getData(),0,dp.getLength());

if("886".equals(data))
{
System.out.println(ip+"....离开聊天室");
break;
}

System.out.println(ip+":"+data);
}
}
catch (Exception e)
{
throw new RuntimeException("接收端失败");
}
}
}

class  ChatDemo
{
public static void main(String[] args) throws Exception
{
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10002);

new Thread(new Send(sendSocket)).start();
new Thread(new Rece(receSocket)).start();

}
}

TCP传输应用练习:

import java.io.*;
import java.net.*;
/**
需求:
演示tcp的传输的客户端和服务端的互访,客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
客户端:
(1)建立socket服务。指定要连接主机和端口。
(2)获取socket流中的输出流。将数据写到该流中。通过网络发送给服务端。
(3)获取socket流中的输入流,将服务端反馈的数据获取到,并打印。
(4)关闭客户端资源。
*/<pre name="code" class="java">class TcpClient2
{
public static void main(String[] args)throws Exception
{
Socket s = new Socket("192.168.1.254",10004);

OutputStream out = s.getOutputStream();

out.write("服务端,你好".getBytes());
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));

s.close();
}
}
class TcpServer2
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10004);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
OutputStream out = s.getOutputStream();
Thread.sleep(10000);
out.write("哥们收到,你也好".getBytes());
s.close();
ss.close();
}
}


4、应用

应用1:
/* *
需求:并发上传图片
*/
import java.io.*;
import java.net.*;
//客户端
class  PicClient
{
public static void main(String[] args) throws Exception
{
//对传入的值进行判断
if (args.length!=1)
{
System.out.println("请指定一个图片文件!");
return;
}

File file=new File(args[0]);

//对文件路径进行判断
if (!(file.exists()&&file.isFile()))
{
System.out.println("你上传的文件有问题,非文件或者不存在!");
return;
}

//判断是否是图片文件
if (!file.getName().endsWith(".jpg"))
{
System.out.println("图片格式错误,请重新选择!");
return;
}

//对文件大小进行判断
if (file.length()>1024*1024*5)
{
System.out.println("你上传的文件过大,居心叵测!");
return;
}

//创建服务
Socket s=new Socket("localhost",10000);
//读取图片数据
FileInputStream fis=new FileInputStream(file);

//用Socket服务输出流写入数据
OutputStream out =s.getOutputStream();

BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));

byte[] buf=new byte[1024];

int len=0;

while ((len=fis.read(buf))!=-1)
{
out.write(buf,0,len);
}

//结束标记,表示文件数据已经上传完了
s.shutdownOutput();

String info=in.readLine();//读取返回信息
System.out.println(info);

fis.close();//关流
s.close();

}
}

//服务端
class PicServer
{
public static void main(String[] args)throws Exception
{
//创建服务,监听端口
ServerSocket ss=new ServerSocket(10000);

while (true)
{
//获取客户端对象
Socket s=ss.accept();
//客户端执行线程
new Thread(new PicThread(s)).start();
}

//ss.close();
}
}

//利用多线程实现并发上传
class PicThread implements Runnable
{
private Socket s;
PicThread(Socket s)
{
this.s=s;
}
public void run()
{
int count=1;
//获取客户端ip
String ip=s.getInetAddress().getHostAddress();
try
{
System.out.println(ip+"  connected.....");

//通过客户端的读取流读取数据
InputStream in=s.getInputStream();
//文件保存路径
File dir =new File("C:\\Users\\asus\\Desktop");
//文件名
File file=new File(dir,ip+".jpg");
//判断文件是否存在
while(file.exists())
{
file=new File(dir,ip+"("+(count++)+").jpg");
}

//将数据写入到指定文件中
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(ip+"图片上传失败");
}
}
} <pre name="code" class="java">小结:
1、一对一(单线程)上传的思路:
客户端
a、服务端点。
b、读取客户端已有的图片数据
c、通过Socket输出流将数据发给服务端
d、读取服务端反馈信息。
e、关闭
服务端
a、服务端服务,并监听窗口
b、获取客户端对象,并获取客户ip
c、读取客户端输入流数据
d、写入文件
e、用客户端输出流反馈信息
f、关流
2、单线程的服务端有个局限性。
当A客户端连接上以后,被服务端获取到。服务端执行具体流程。这时B客户端连接,只能等待。因为服务端还没有处理完A客户端的请求。还没有循环回来执行下一次accept方法。所以,暂时获取不到B客户端对象。
那么为了可以让多个客户端同时并发访问服务端。服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。
如何定义线程呢?
只要明确了每一个客户端要在服务端执行的代码,如示例所示将该代码存入run方法即可。
*/<span style="font-family: Arial, Helvetica, sans-serif;"> </span>


应用2:
import java.io.*;
import java.net.*;
//客户端
class  LoginClient
{
public static void main(String[] args) throws Exception
{
//创建服务
Socket s=new Socket("localhost",10000);
//键盘录入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

//用Socket服务输出流写入数据
PrintWriter out =new PrintWriter(s.getOutputStream(),true );

//接收服务器返回的信息
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));

String line=null;

for(int x=0;x<3;x++)
{
line=br.readLine();//读取键盘录入
if (line==null)
{
break;//如果键盘没有输入,则直接结束
}

out.println(line);//将数据写入流中

String info=in.readLine();//读取返回信息

System.out.println(info);

if (info.contains("欢迎"))//---------------
{
break;//如果登录成功,就跳出循环
}
}
br.close();//关流
s.close();
}
}

//服务端
class LoginServer
{
public static void main(String [] args)throws Exception
{
//创建服务,监听端口
ServerSocket ss=new ServerSocket(10000);

while (true)
{
//获取客户端对象
Socket s=ss.accept();

//客户端执行线程
new Thread(new LoginThread(s)).start();
}

//ss.close();
}
}

//利用多线程实现并发登录
class LoginThread implements Runnable
{
private Socket s;
LoginThread(Socket s)
{
this.s=s;
}
public void run()
{
//获取客户端ip
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"  connected.....");
try
{
for (int x=0;x<3 ;x++ )
{
//通过客户端的读取流读取数据
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));

//读取数据库中的数据,这里用文件来表示数据库
BufferedReader br=new BufferedReader(new FileReader("users.txt"));

String line=in.readLine();//读取客户端数据
if (line==null)//--------------
{
break;//如果客户端没有发送数据,则跳出循环
}
String data=null;
boolean flag=false;//设置标记
//读取数据库中的用户数据
while ((data=br.readLine())!=null)
{
if (line.equals(data))
{
flag=true;//如果用户存在,则将标记设为true
break;
}
}

//将数据写入到指定文件中
PrintWriter out=new PrintWriter(s.getOutputStream(),true);

if (flag)
{
System.out.println(line+",已登陆!");

out.println(line+",欢迎光临!");

break;//-----------
}
else
{
System.out.println(line+",尝试登陆!");
out.println(line+",用户名不存在!");
}
}
s.close();//关流
}
catch (Exception e)
{
throw new RuntimeException("用户登陆失败");
}
}
}<pre name="code" class="java">/**
小结:
(1)浏览器是一个标准的客户端,它可以对服务端传送过来的数据消息进行解析,把符合应用层协议的消息部分解析后,将头信息拆包掉,传送到应用层,只保留了正确的正文主题部分显示在主体部分上。
(2)而由于使用java编译是在传输层和网际层处理的,所以,会接受到全部的消息,包含了头消息。而浏览器处于应用层,已将发送来的头消息去除,只留下了主体信息。
*/<span style="font-family: Arial, Helvetica, sans-serif;">  </span>


三、URL和URLConnection

1、概述

/**
(1)URL:
URI:范围更大,条形码也包含于此范围
URL:范围较小,即域名
1)方法
a、构造函数:URL(String protocol,String host,int port,String file);//根据指定 protocol、host、port号和 file 创建 URL对象。
b、String getProtocol();//获取协议名称
c、String getHost();//获取主机名
d、int getPort();//获取端口号
e、String getFile();//获取URL文件名
f、String getPath();//获取此URL的路径部分
g、String getQuery();//获取此URL的查询部,客户端传输的特定信息
注:一般输入网址,是不带端口号的,此时可进行获取,通过获取网址返回的port,若port为-1,则分配一个默认的80端口,如
int port = getPort();
if(port == -1)
port = 80;
(2)URLConnection
1)方法:
a、URLConnection openConnection();//用URL调用此方法,返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
b、InputStream getInputStream();//获取输入流
c、OutputStream getOutputStream();//获取输出流
*/

练习1:

/* *
自定义浏览器,显示网页信息
*/

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;

class MyIEGUIDemo
{
//定义所需组件引用
private Frame f;
private Button but,bok;
private TextField tf;
private TextArea ta;

//构造函数
MyIEGUIDemo()
{
init();
}

//窗体基本设置于功能实现
public void init()
{
//组件实例化
f=new Frame("我的Window");
but=new Button("跳转");
tf=new TextField(50);
ta=new TextArea(25,60);

//基本设置
f.setBounds(300,150,500,500);
f.setLayout(new FlowLayout());

//添加组件
f.add(tf);
f.add(but);
f.add(ta);

//窗体事件
myEvent();

//窗体显示
f.setVisible(true);
}

//注册事件
public void myEvent()
{
//窗体关闭功能
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});

//“跳转”按钮事件
but.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
showFile();//显示网页内容在文本区中
}
});

//文本框键盘事件
tf.addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e)
{
//如果键盘按下Enter键,就将网页内容显示在文本区中
if(e.getKeyCode()==KeyEvent.VK_ENTER)
showFile();
}
});
}

//显示网页内容
private void showFile()
{
ta.setText("");
String path=tf.getText();//获取输入的路径
try
{
//封装地址对象
URL url =new URL(path);
连接网页服务器
URLConnection conn=url.openConnection();
//读取流,用于读取服务器返回数据
InputStream in=conn.getInputStream();

byte[] buf=new byte[1024*1024];

int len=in.read(buf);
//将数据显示在文本区中
ta.append(new String(buf,0,len));
}
catch (Exception e)
{
throw new RuntimeException("连接"+path+"网站失败");
}
}

public static void main(String[] args)
{
//运行窗体
new MyIEGUIDemo();
}
}

2、小知识点扩展

/**
(1)InetSocketAddress对象(IP+端口)
(2)ServerSocket对象中的构造函数:
ServerSocket(int port,int backlog),其中的backlog表示队列的最大长度,即最多连入客户端的个数,即最大连接数。
(3)在进行浏览器输入网址访问一台主机所做的操作:
如http://192.168.1.100:8080/myweb/myIE.html,一般直接输入主机名:http://baidu.com.cn等,那么如何通过主机名获取IP地址,从而连接到这台主机的呢?这就需要将主机名翻译成IP地址,即域名解析:DNS。
在进行访问的时候,会现在本地的hosts文件(C:\WINDOWS\system32\drivers\etc\hosts)中找对应的映射,若有,则直接返回请求,若无,则到公网的映射列表即DNS中找对应的映射,找到后,将主机名对应的IP地址返回给本机,本机通过这个IP地址找到对应的服务器。
*/

图片说明:



host的应用:
(1)可屏蔽一些恶意网址,即将对应的映射关系写入hosts中,将IP地址改为本机的回环地址,那么会直接找到hosts,就不会将请求发送出去了。
(2)可以提高上网速度。

好了,大神们,现在是下午17:11分,肚子有些饿了,准备晚餐去了,有一起的吗?那就吃饭饭了,嘎嘎。

------- android培训java培训、期待与您交流!
---------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: