黑马程序员-网络编程
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
概念:
网络模型分两个:OSI(Open System Interconnection开放系统互联)参考模型、TCP/IP参考模型。
网络传输的要素有三个:ip地址、端口、传输协议。
ip地址是网络中各种设备的标识,有了ip就能准确的找到某一台设备,它也可以是主机名,因为主机名和ip存在映射关系。
端口是用于标识进程的逻辑地址,它就是一个整数,范围是0~65535,其中0~1024为系统保留或使用端口。
传输协议(通讯规则)又分为:
1.UDP协议
udp协议会将数据及源和目的封装到数据包中,不需要建立连接,每个数据包的大小限制在64k内,因为是面向无连接协议,所以是不可靠的(容易丢失数据),因此也是速度较快的。常见的udp协议应用如:网络视频、聊天、桌面共享。
2.TCP协议
tcp协议是面向连接的协议,在传输数据前需要通过三次握手来建立通道,从而完成大量数据的传输,正因为要建立连接才可以传输数据,所以它是可靠协议(不会丢失数据),因此也是效率较低的。常见的tcp协议应用如:下载。
常见网络结构:
1.C/S结构
该结构的软件,客户端和服务端都需要编写,开发成本较高,且维护较为麻烦。但是这种结构的软件可以在本地分担一部分计算,减轻服务器压力。
2.B/S结构
该结构的软件,只需要开发服务端,因为客户端都有浏览器取代,而系统就自带了浏览器,所以这种结构相对来说开发和维护成本较低。
代码及示例:
1. UDP协议:
public class UDPSend {
/**
* UDP协议发送端
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
DatagramSocket ds=new DatagramSocket(10087);//用于UDP协议的对象,如果不指定发送端端口,系统会默认随机生成。
String line=null;
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
while((line=br.readLine())!=null){
if("over".equals(line))//结束标记,因为是键盘录入。
break;
byte[] buf=line.getBytes();
DatagramPacket packet=new DatagramPacket(buf, 0, buf.length, InetAddress.getByName("127.0.0.1"), 10086);//指定数据包要发送的目的ip或端口号
ds.send(packet);//发送数据,如果接收端未开启,数据将丢失。
}
//关闭发送端
ds.close();
}
}
public class UDPReceive {
/**
* UDP协议接收端
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//创建udp协议,接收端必须指定其监听的端口。
DatagramSocket ds=new DatagramSocket(10086);
//创建数据包
byte[] buf=new byte[1024];
DatagramPacket packet=new DatagramPacket(buf, buf.length);//如果发送的数据超出该数据包长度,那么超出的部分数据将被遗弃。
while(true){
ds.receive(packet);//接受数据,该方法为阻塞式的。该方法每次在接受数据时,会将数据包中数据清空,所以不必每次创建数据包。
String ip=packet.getAddress().getHostAddress();//获取发送端的ip
int port=packet.getPort();//获取发送端端口
System.out.println(ip+":::"+new String(packet.getData(),0,packet.getLength())+" 端口"+port);
}
}
}
用多线程实现udp协议:
//用多线程实现udp发送端
public class Send implements Runnable {
private DatagramSocket ds;
public Send(DatagramSocket ds){
this.ds=ds;
}
@Override
public void run() {
try {
String line = null;
BufferedReader br = new BufferedReader(new InputStreamReader(
System.in));
while ((line = br.readLine()) != null) {
if ("over".equals(line))// 结束标记,因为是键盘录入。
break;
byte[] buf = line.getBytes();
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length,
InetAddress.getByName("127.0.0.1"), 10086);// 指定数据包要发送的目的ip或端口号
ds.send(packet);// 发送数据,如果接收端未开启,数据将丢失。
}
ds.close();
} catch (Exception e) {
}
}
}
//多线程实现udp接收端
public class Receive implements Runnable {
private DatagramSocket ds;
public Receive(DatagramSocket ds){
this.ds=ds;
}
@Override
public void run() {
try {
// 创建数据包
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);// 如果发送的数据超出该数据包长度,那么超出的部分数据将被遗弃。
while (true) {
ds.receive(packet);// 接受数据,该方法为阻塞式的。该方法每次在接受数据时,会将数据包中数据清空,所以不必每次创建数据包。
String ip = packet.getAddress().getHostAddress();// 获取发送端的ip
int port = packet.getPort();// 获取发送端端口
System.out.println(ip + ":::"
+ new String(packet.getData(), 0, packet.getLength())
+ " 端口" + port);
}
} catch (Exception e) {
}
}
}
public class UDPTest {
/**UDP聊天程序,多线程实现发送端和接收端。
* @param args
* @throws SocketException
*/
public static void main(String[] args) throws SocketException {
DatagramSocket send=new DatagramSocket(10087);//接收端
DatagramSocket receive=new DatagramSocket(10086);//发送端
new Thread(new Send(send)).start();//启动发送端
new Thread(new Receive(receive)).start();//启动接收端
}
}
2.TCP协议:
public class TCPServer {
/**
* TCP服务端
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//创建TCP服务端
ServerSocket server=new ServerSocket(10086);
//获取客服端Socket对象
Socket s=server.accept();//阻塞式方法
//获取Soket输入流,读取客户端数据。
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//获取Soket输出流,给客户端返回数据。
PrintWriter out=new PrintWriter(s.getOutputStream(),true);
//读取客户端数据并打印在控制台,并将数据转换后返回给客户端。
String line=null;
while((line=br.readLine())!=null){
System.out.println(line);
out.println(line.toUpperCase());
}
//关闭客户端
s.close();
//关闭服务端
server.close();
}
}
public class TCPClient {
/**
* TCP客户端
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException, IOException {
//创建TCP客户端,需要在创建时明确连接。因为TCP协议是面向连接的。如果创建时未指定连接主机,可以通过connect()方法指定。
Socket s=new Socket("127.0.0.1",10086);
//获取客户端键盘录入
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//获取Scoket中的输出流,保证数据原样性用PrintWriter
PrintWriter out=new PrintWriter(s.getOutputStream(),true);//如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区
//获取Scoket中输入流,读取从服务端返回的数据。
BufferedReader brIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
//将客户端输入数据写入到Socket流中
String line=null;
while((line=br.readLine())!=null){
if("over".equals(line))//键盘录入结束标记
break;
out.println(line);
//读取服务端返回的数据
String upperStr=brIn.readLine();
System.out.println(upperStr);
}
//关闭资源,System.in不需关闭。这句话其实就是往Socket流中写入结束标记。
s.close();
}
}
TCP上传文件:
public class UploadClient {
/**
* TCP协议上传文件
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException, IOException {
//创建客户端
Socket s=new Socket("127.0.0.1",10086);
//创建流关联要上传的文件
BufferedReader br=new BufferedReader(new FileReader("f:\\list.txt"));
//获取Scoket输出流
PrintWriter out=new PrintWriter(s.getOutputStream(),true);
//上传文件
String line=null;
while((line=br.readLine())!=null){
out.println(line);
}
//由于文件读完后,没有往Socket流中写入结束标记,所以要告知服务端文件已读完。
s.shutdownOutput();//往Socket输出流写入结束标记
//获取Socket输入流,读取服务端反馈的信息。
InputStream in=s.getInputStream();
byte[] buf=new byte[1024];
int len=in.read(buf);
String result=new String(buf, 0, len);
System.out.println(result);
//关闭资源
s.close();
br.close();
}
}
public class UploadServer {
/**
* TCP上传文件服务端
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//创建服务端
ServerSocket ss=new ServerSocket(10086);
//获取客户端
Socket s=ss.accept();
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"........connected");
//获取Socket输入流读取客户端数据
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//创建流保存客户端数据
BufferedWriter bw=new BufferedWriter(new FileWriter("f:\\server.txt"));
//保存数据
String line=null;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
}
//获取Socket输出流,给客户端反馈信息。
OutputStream out=s.getOutputStream();
out.write("上传成功!".getBytes());
//关闭资源
ss.close();
s.close();
bw.close();
}
}
TCP多线程上传文件:
public class TCPUploadClient {
/**
* TCP多线程上传客户端
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//创建客户端
Socket s=new Socket("127.0.0.1",10086);
//创建流关联上传的文件
FileInputStream fis=new FileInputStream("f:\\0.jpg");
//获取Socket输出流
OutputStream out=s.getOutputStream();
//上传数据
int len=0;
byte[] buf=new byte[1024];
while((len=fis.read(buf))!=-1){
out.write(buf,0,len);
}
//告诉服务端数据已经写完
s.shutdownOutput();
//读取服务端反馈的信息。
InputStream in=s.getInputStream();
byte[] bufIn=new byte[1024];
int lenIn=in.read(bufIn);
System.out.println(new String(bufIn,0,lenIn));
}
}
public class TCPUpload {
/**
* TCP多线程上传服务端
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//创建服务端
ServerSocket ss=new ServerSocket(10086);
//为了给多个客户服务,所以循环。
while(true){
Socket s=ss.accept();
//为了避免在处理某个客户数据时,其他客户连不上(等待),需要将处理过程封装成单独任务。
new Thread(new UploadTask(s)).start();
}
}
}
//将处理数据的过程单独封装成任务即线程
public class UploadTask implements Runnable {
private Socket s;
public UploadTask(Socket s){
this.s=s;
}
@Override
public void run() {
int count=0;//给服务器上同名文件编号
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"...........connected");
File dir=new File("f:\\upload");//创建保存上传文件的目录
if(!dir.exists()){
dir.mkdirs();
}
File file=new File(dir,ip+".jpg");
//检查同名文件
while(file.exists()){
file=new File(dir,ip+"("+(++count)+").jpg");
}
try {
// 获取Socket输入流读取客户端数据
InputStream in = s.getInputStream();
// 创建流保存上传文件
FileOutputStream fos = new FileOutputStream(file);
int len=0;
byte[] buf=new byte[1024];
while((len=in.read(buf))!=-1){
fos.write(buf, 0, len);
}
//获取Socket输出流,反馈信息给客户端。
OutputStream out=s.getOutputStream();
out.write("上传成功!".getBytes());
//关闭资源
fos.close();
s.close();
} catch (Exception e) {
}
}
}
URLConnection对象:
public class URLConnectionDemo {
/**
* 模拟一个简单的浏览器
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// URLConnection 对象封装了能解析具体协议的对象和Socket
String urlStr="http://localhost:8080/school/zhonghe.html";
URL url=new URL(urlStr);
//获取url连接器
//URLConnection conn=url.openConnection();
//InputStream in=conn.getInputStream();
//获取能解析具体协议的Socket流。
InputStream in=url.openStream();//openStream()方法是下面方法的缩写: openConnection().getInputStream()
int len=0;
byte[] buf=new byte[1024];
while((len=in.read(buf))!=-1){
System.out.println(new String(buf,0,len,"utf-8"));
}
}
}
总结:
TCP、UDP是传输层的协议,而http协议是应用层的协议。在编写基于TCP协议程序时要注意,当往Socket流中写入的数据结束时,应该调用Socket的shutdownOutput或shutdownInput方法在流末尾写入结束标记来告知服务器上传数据已经完毕。如果在基于TCP协议传输过程中出现客户端和服务端相互等待的情况,应该首先考怒是否读到了结束标记。
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------