黑马程序员——网络编程
来源:互联网 发布:wow7.0前夕插件mac 编辑:程序博客网 时间:2024/05/17 08:46
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
网络就是数据传输,跟IO一样,只是在网络上进行数据传输
网络通讯的要素:
1、IP地址
2、端口
3、协议
步骤:
1、选择相关的协议
(TCP(Socket),UDP(DatagramSocket,DatagramPacket))
2、确定目的的IP地址和端口
InetAddress
|–Inet4Address
|–Inet6Address
方法:
getLocalhost();获取本机ip地址主机名
getHostAddress();主机地址,不包含主机名
getByName(host);通过主机名获取ip地址(常用)
getAllByName(host);获取这个主机名下的所有地址(www.baidu.com,就不止一个IP地址)
Udp:用户数据报协议
1、面向无连接
2、数据包小于64k
3、传输速度快
4、数据源和目的地址和数据封装在数据包中
5、UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境
如:网络直播,网络视频会议等
UDP建立Socket使用 :DatagramSocket
传输数据包使用:DatagramPacket
UDP无论客户端还是服务端都是使用DatagramSocket,不像TCP有区分
"/**
客户端
*/"
publicstatic void udpDemo() {
new Thread(new Runnable() {
@Override
public void run() {
try {
"// 1、建立socket连接,发送端指定端口为10000"
DatagramSocket ds = newDatagramSocket(10000);
"// 2、创建数据包,创建要发送的数据"
byte[] by = "abcdefg".getBytes();
"// 设置dp的发送数据内容,长度,目标地址,目标端口"
DatagramPacket dp = newDatagramPacket(by, by.length,
InetAddress.getByName("192.168.1.189"),10001);
"// 3、socket发送dp中的数据"
ds.send(dp);
"// 4、关闭资源"
ds.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostExceptione) {"// 未知主机异常"
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
"/**
服务端
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
"// 1、建立socket连接,接收端指定端口为10000"
DatagramSocket ds = newDatagramSocket(10001);
"// 2、创建数据包,并创建byte缓冲区,接收数据大小"
byte[] by = new byte[1024];
"// 设置接收端的存储数组,存储的长度"
DatagramPacket dp = newDatagramPacket(by, by.length);
"// 3、把接受的数据存入dp中,receive()方法是一个阻塞式方法,若没有接收数据,则会一直阻塞,直到接受到数据"
ds.receive(dp);
"// dp.getData()接收到的数据是byte[]类型的"
System.out.println(dp.getPort()
+ newString(dp.getData(), 0, dp.getLength()) + " : "
+ dp.getLength() + ": " + dp.getAddress() + " : ");
ds.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostExceptione) {"// 未知主机异常"
e.printStackTrace();
} catch (IOException e) { e.printStackTrace();
}
}
}).start();
}
键盘聊天程序
publicstatic void udpDemo2() {
"/**
* 发送端
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
"//创建DatagramSocket对象,并指定端口"
DatagramSocket ds = newDatagramSocket(2004);
BufferedReader br = newBufferedReader(new InputStreamReader(
System.in));
String line = null;
while ((line =br.readLine()) != null) {
"// 需要放在while内部,line是读取的数据,在readLine()是收到数据赋值给line"
DatagramPacket dp = newDatagramPacket(line.getBytes(),
line.length(),InetAddress.getByName("192.168.1.189"),
2003);
ds.send(dp);
"// 先发送之后再判断是不是结束,若是放在开头则会出现发送端断开,接受断不断开,因为数据没有发送出去"
if ("over".equals(line))
break;
}
ds.close();
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
"/**
* 接收端
* 注意:
* new String(dp.getDate(),0,dp.getLength())要指定从0开始,否则出现数据异常
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
DatagramSocket ds = newDatagramSocket(2003);
byte[] by = new byte[1024];
"// 定义在外面不用每一次都重新建立一个数据包"
DatagramPacket dp = new DatagramPacket(by,by.length);
while (true) {
ds.receive(dp);
String data = newString(dp.getData(), 0, dp.getLength());
"// 判断是不是结束标示"
if (data.equals("over"))
break;
System.out.println(data+ ":" + dp.getAddress());
}
"// 关闭资源"
ds.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
TCP:传输控制协议
1、面向连接,使用三次握手机制
2、链接稳定,可靠性高
3、主要用于传输大文件数据
如:下载大文件
1、客户端使用的是:Socket,一开始可以明确服务端的ip和端口
2、服务端使用的是:ServerSocket,明确自己的端口,(不需要ip指定地址),可以指定同时连接的最大数
3、为了区分不同客户端之间的数据,服务端的accept()方法获取的是客户端的socket流对象,因为要操作的数据客户端自己最清楚,使用客户端的Socket对象来操作数据
Socket流在Socket对象一建立的时候就自动创建了
getInputStream()获取输入流
getOutputStream()获取输出流
注意:服务端必须要先打开,客户端才能开始链接并发送数据
ServerSocket ss = new ServerSocket(2005,3);3是指定客户端同时连接进来的最大数
Socket属于传输层
publicstatic void tcpDemo1() {
"/**
* 服务端
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
"// 1、建立ServerSocket对象,并确定端口号"
ServerSocket ss = newServerSocket(2005);
"/**
* 2、返回一个socket对象,这个对象就是链接进来的客户端对象
* 使用链接进来的客户端对象来区分不同客户端数据
* ss.accept()是一个阻塞式方法,没有客户端链接进来的时候阻塞,不会消耗资源
*/"
Socket s = ss.accept();
"// 3、获取客户端发来的数据,然后就是io操作"
InputStream in =s.getInputStream();
int len = 0;
while ((len = in.read()) !=-1) {
System.out.print((char)len);
}
"// 服务端必须关闭资源,防止资源被持续占用"
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
"/**
* 客户端
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
"// 1、建立Socket对象,同时会自动产生Socket流"
Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2005);
"// 2、获取输出流,就是客户端要发送数据到服务端"
OutputStream out =s.getOutputStream();
"// 3、传输的数据是字节流"
out.write("abcdefg".getBytes());
"// 4、客户端关闭链接"
s.close();
} catch (UnknownHostExceptione) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
文件转换器:
客户端发送一段字母,服务端变为大写并返回给客户端
客户端可以一直发送,直到发送over,并且over不会被转换并打印
必须要注意的问题是回车符 问题
readLine()会读取回车符之前的数据,不包含回车符
当使用readline()时:一旦按下回车符,客户端会发送数据,若是使用了缓存必须要先flusd()才会发送数据
发送的数据中是不包含回车符,服务器接收端的readLine()方法读取不到回车符就会一直等待(阻塞式方法)
所以在发送数据时必须要在write()中手动加入回车符:\r\n
不使用readLine()时候,而是使用自定义缓存也会产生回车符 问题
自定义缓存会发送你所有的输入数据,此时你在客户端按下回车,这时候会发送数据
但是同时发送的数据中会加入回车符 (比你输入的字符多两个字符)
回车符也会传入到服务器端,這時候輸出的數據就會自动换行,因为有回车符
同时进行over判断的时候也会返回false,因为多了回车符
public static void tcpToUpperCaseDemo() {
"/**
* 服务端
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
"// 建立serversocket连接,指定端口号"
ServerSocket ss = newServerSocket(2011);
"// 获取链接进来的客户端socket对象"
Socket s = ss.accept();
System.out.println("获取链接:" +s.getInetAddress());
"/ 定义输入流和输出流的缓冲区对象"
BufferedReader br = newBufferedReader(new InputStreamReader(
s.getInputStream()));
BufferedWriter bw = newBufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
int num = 0;
char[] by = new char[1024];
while ((num = br.read(by)) != -1) {
"// 这里并不需要num-2,因为在数据传输过来的时候已经去掉了回车符"
String message = newString(by, 0, num);
System.out.println("收到数据是:"+ message);
"// 返回大写数据"
bw.write(message.toUpperCase());
bw.flush();
}
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
"/**
* 客户端
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
"// 建立客户端socket连接,指定服务器ip地址和端口"
Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2011);
"// 定义socket流的输入流"
BufferedReader br = newBufferedReader(new InputStreamReader(
s.getInputStream()));
"// 定义socket流的输出流"
BufferedWriter bw = newBufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
"// 定义键盘的输入流"
BufferedReader in = newBufferedReader(new InputStreamReader(
System.in));
int num = 0;
int snum = 0;
char[] ch = new char[1024];
char[] sin = new char[1024];
System.out.println("开始录入数据:");
while ((snum =in.read(sin)) != -1) {
"// 把获取的数据变成字符串,同时 -2 是把输入数据的回车符(\r\n)去掉,否则下面的判断返回false"
String message = newString(sin, 0, snum - 2);
System.out.println("发送数据数据:"+ message);
"// 发送数据"
bw.write(message);
"// 必须要flush(),因为使用缓存默认会保存在缓存中,刷新后才会正式发送出去"
bw.flush();
"// 定义接收socket返回的数据"
while ((num =br.read(ch)) != -1) {
String back = newString(ch, 0, num);
System.out.println(back);
"// 当返回了一次数据后就不用接收了,因为没有数据,所以用break跳出"
break;
}
if ("over".equals(message)){
break;
}
}
System.out.println("结束+1");
in.close();
s.close();
} catch (UnknownHostExceptione) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
tcp图片上传
public static void photoUp() {
"/**
*服务器端
*/"
newThread(new Runnable() {
@Override
public void run() {
try {
ServerSocket ss = newServerSocket(2003, 3);
Socket s = ss.accept();
System.out.println("链接:" +s.getInetAddress());
"获取网络对象输入流"
InputStream in =s.getInputStream();
FileOutputStream fos = newFileOutputStream("c://demo//0.mp3");
int len = 0;
byte[] by = new byte[1024];
while ((len = in.read(by)) != -1){
fos.write(by, 0, len);
}
"//上传完毕后返回信息给客户端"
out.write("上传完毕!!".getBytes());
fos.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
"/**
* 客户端
*/"
newThread(new Runnable() {
@Override
public void run() {
try {
"//建立Socket"
Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2003);
"获取网络兑现输出流"
OutputStream out =s.getOutputStream();
FileInputStream in = newFileInputStream("c://demo//love.mp3");
"获取网络对象输入流"
BufferedReader in = newBufferedReader(new InputStreamReader(s.getInputStream()));
int len = 0;
byte[] by = new byte[1024];
while ((len = in.read(by)) != -1){
out.write(by, 0, len);
out.flush();
}
"//通知服务器上传完成,否则服务器一直等待"
s.shutdownOutput();
"获取服务器返回的数据"
String line = in.readLine();
System.out.println(line);
in.close();
s.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
实现多个客户端同时连接服务器
publicstatic void servers() {
"/**
* 服务端
*/"
new Thread(new Runnable() {
@Override
public void run() {
try {
ServerSocket ss = new ServerSocket(2004);
"// 创建3个连接"
for (int i = 0; i < 3;i++) {
Socket s = ss.accept();"accept()是阻塞式方法,当没有客户端连接时进行阻塞"
methoserver(s, i);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
"/**
* 客户端,创建3个客户端,同时连接
*/"
for (int i = 1; i < 4; i++) {
methodclient(i);
}
}
"/**
* 封装服务器的共性方法
* @param s
*/"
publicstatic void methoserver(final Socket s, final int count) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("链接:" +s.getInetAddress());
InputStream in;
try {
in = s.getInputStream();
FileOutputStream fos = newFileOutputStream("c://demo//" + count
+ "1.mp3");
int len = 0;
byte[] by = new byte[1024];
while ((len = in.read(by))!= -1) {
fos.write(by, 0, len);
}
fos.close();
s.close();
System.out.println("客户端连接断开");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
"/**
* 封装客户端方法
* 创建一个客户端,并上传数据
*/"
publicstatic void methodclient(final int count) {
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("创建一个客户端:"+ Thread.currentThread());
Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2004);
OutputStream out =s.getOutputStream();
System.out.println("开始读取文件...");
FileInputStream in = newFileInputStream("c://demo//" + count
+ ".mp3");
int len = 0;
byte[] by = new byte[1024];
while ((len = in.read(by))!= -1) {
out.write(by, 0, len);
out.flush();
}
System.out.println("数据发送完毕");
in.close();
s.close();
System.out.println("连接断开:" +Thread.currentThread());
} catch (UnknownHostExceptione) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
多线程下载文件:
HttpURLConnection:
通过URL获取网络资源的位置,内部封装了Socket的相关方法,getInputStream()和getOutputStream()
conn.connect();连接
conn.disconnect();断开连接,Socket也会断开
实现方式:
1、获取网络文件的大小
2、在本地通过RandomAccessFile 创建一个文件,大小为网络文件大小,关闭网络资源
3、开启线程,在每一个线程中下载网络文件的一段数据
* |–1、首先通过httpURLconnection连接网络资源
* |–2、设置网络的获取方法,超时时间等
* |–3、通过setRequestProperty(“Range”, “bytes=”+start+”-“+end);设置下载数据的开始和结尾(格式绝对不能写错)
*
HTTP 协议的 Range 头可以指定从文件的什么位置开始下载,下载到什么位置结束。单位为 byte ,数据范围包含头不包含尾.
假如 Range 指定要读取到文件的 5104389 的字节数位置,但是下载的文件本身只有 4104389 个长度。
那么下载操作自动会在 4104389 处停止,因此不会下载到多余的无效数据.
4、开始下载数据
public static void httpUrlConnectionDemo(){
try{
"// url获取远程网络对象"
URL url = new URL("http://127.0.0.1:8080/demo.avi");
"获取HttpURLConnection 对象"
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(3000);
conn.setRequestMethod("GET");
conn.connect();
"// 获取远程对象的文件大小"
int length = conn.getContentLength();
conn.disconnect();
"// 创建本地文件,并进行健壮性判断"
File file = new File("c://demo/ccc.avi");
if (file.exists()) {
file.delete();
file.createNewFile();
} else {
file.createNewFile();
}
"// 设置文件的长度"
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.setLength(length);
"// 开启3个线程进行下载"
int start = 0;
int end = 0;
"// 获取每一段结尾的长度"
int[] lenend = new int[] { length / 3, length / 3 * 2, length };
for (int i = 0; i < 3; i++) {
end = lenend[i];
"// 开启线程并进行下载"
threadDownload(start, end);
start = end;
}
}catch (Exception e) {
e.printStackTrace();
}
}
"/**
* 开始进行下载
*@param start
*@param end
*/"
public static void threadDownload(final intstart, final int end) {
newThread(new Runnable() {
@Override
public void run() {
URL url = null;
File file = null;
HttpURLConnection conn = null;
try {
"// 获取远程网络文件对象"
url = new URL("http://127.0.0.1:8080/demo.avi");
conn = (HttpURLConnection)url.openConnection();
"//设置超时时间"
conn.setReadTimeout(3000);
"//设置方法"
conn.setRequestMethod("GET");
"// 设置获取的数据范围, 重点"
conn.setRequestProperty("Range","bytes=" + start + "-" + end);
conn.connect();
} catch (Exception e1) {
e1.printStackTrace();
}
try {
"// 输出文件的目地地址"
file = new File("c://demo/ccc.avi");
"// 创建随机文件读写对象,并声明权限"
RandomAccessFile raf = newRandomAccessFile(file, "rwd");
"// 设置文件的指针位置从那里开始:重要,不设置默认从0开始写入"
raf.seek(start);
"// 开始下载数据"
InputStream in =conn.getInputStream();
int len = 0;
"对文件进行写入"
while ((len = in.read(by))!= -1) {
raf.write(by, 0, len);
}
System.out.println(start + " : " + end);
System.out.println(file.length()+ " : " + Thread.currentThread());
raf.close();
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
- 黑马程序员— 网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员 — 网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 黑马程序员—网络编程
- 6个Linux chkconfig命令实例 - 增加,删除,查看和修改services的自动启动选项
- Activity启动模式总结
- 面向过程设计和面向对象设计之间区别的实例
- editplus快捷键大全
- 关于google play ,facebook ,amazon SDK集成的经历
- 黑马程序员——网络编程
- 【JQuery】write less,do more
- pycharm4.5.1 注册码 破解
- 谈谈我自己(创业四个多月)
- Rails接口(interface)入门
- html头部
- gvim配置及相关插件安装(过程详细,附图)
- Handler 刷新
- Chrome 浏览器主页被 hao123 篡改解决办法