黑马程序员————网络Socket编程

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


网络编程概述

要素:ip地址,端口号,传输协议

模型:OSI参考型,TCP/IP参考模型

黑马程序员————网络Socket编程_第1张图片


网络模型


黑马程序员————网络Socket编程_第2张图片


应用层:数据表现规律,及应用层的特征

TCP UDP在传输层。

IP地址协议在网络层。


数据链路层:底层传输协议。

物理层:网线


数据封包:加上每一层的特有信息。

数据传输就是通过数据封包,拆包这样完成的。


IP地址

package Socket;

import java.net.InetAddress;

public class IPTest {
	public static void main(String[] args)throws Exception{
		//互联网协议的地址
		InetAddress[] in=InetAddress.getAllByName("www.baidu.com");//获得百度的所有IP地址
		for(InetAddress i:in){
		String s=i.getHostName();//获得主机名
		String ss=i.getHostAddress();//获得主机ip
		System.out.println(s+"...."+ss);
		}
	}
}


TCP与UDP

UDP:

1、将数据的源和目的封装成数据包,不需要建立连接

2、每个数据包的大小限制在64k内

3、因为是无连接,是不可靠协议。

4、不需要建立连接,故此速度快-----如视频会议,桌面共享,游戏,QQ

TCP:

1、要建立连接,形成传输数据的通道

2、在连接中进行大数据量传输。

3、通过3次握手完成连接,是可靠协议

4、必须建立连接,速度慢,效率稍低------电话,下载。


UDP发送端

需求:通过udp传输,将一段文字数据发送出去

思路:

1.建立udpSocket服务。

2.提供数据,并将数据封装到数据包中

3.通过socket服务的发送功能,经数据包发送出去。

4.关闭资源。

package Socket;
import java.net.*;
public class UDPSocket {
	public static void main(String[] args)throws Exception{
		//创建udp服务
		DatagramSocket ds=new DatagramSocket();
		//确定数据,封装成数据包
		byte[] buf="udp coming ".getBytes();
		//将数据发送到制定的地址的指定端口上
		DatagramPacket dp=new DatagramPacket(buf,buf.length,InetAddress.getLocalHost(),1000);
		//发送数据包
		ds.send(dp);
		//关闭资源
		ds.close();
	}
}

面向无连接的,如果接受端没开启,那么数据就丢失了。


UDP接收端

需求:定义一个应用程序用于接收udp协议传输的数据并处理。


思路:

1、定义udpSocket服务,通常会监听一个端口,方便明确数据是由哪个应用程序处理。

2、定义一个数据包,因为要存储接收到的字节数组。

3、通过socket服务的receive方法接收到的数据存到已定义好的数据包中。

4、通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。

5、关闭资源。

package Socket;
import java.net.*;
public class UDPSocket {
	public static void main(String[] args)throws Exception{
		//创建udp服务
		DatagramSocket ds=new DatagramSocket();
		//确定数据,封装成数据包
		byte[] buf="udp coming ".getBytes();
		//将数据发送到制定的地址的指定端口上
		DatagramPacket dp=new DatagramPacket(buf,buf.length,InetAddress.getLocalHost(),1000);
		//发送数据包
		ds.send(dp);
		//关闭资源
		ds.close();
	}
}

有了接收端,那么之前发送的数据就可以收到,主要要在接收端开启后,才发送数据,因为是无连接的。


UDP键盘录入

package Socket;
import java.io.*;
import java.net.*;
public class UDPSend {
	public static void main(String[] args)throws Exception{
		//udp服务
		DatagramSocket ds=new DatagramSocket();
		//定义输入流---字节流变字符流
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		//读数据
		String line=null;
		while((line=br.readLine())!=null){
			if("886".equals(line))
				break;
			byte[] buf=line.getBytes();//字符串变字节
			//将数据打包,存到那个ip地址,那个端口号下的
			DatagramPacket dp=new DatagramPacket(buf,buf.length,ds.getInetAddress().getLocalHost(),1000);
			//传输数据
			ds.send(dp);
		}
		ds.close();
	}
}

package Socket;
import java.net.*;
public class UDPResc {
	public static void main(String[] args)throws Exception{
		//udp服务,和发送端的端口关联好
		DatagramSocket ds=new DatagramSocket(1000);
		//定义数据包,用于存储数据
		while(true){
			byte[] buf=new byte[1024];
			DatagramPacket dp=new DatagramPacket(buf,buf.length);
			//存数据
			
			ds.receive(dp);
			
			String ip=dp.getAddress().getHostName();//获得次ip的主机名
			String data=new String(dp.getData(),0,dp.getLength());
			int port=dp.getPort();
			
			System.out.println(ip+"..."+data+"..."+port);
			
		}
	}
}


TCP传输

客户端:

通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机。因为tcp是面向连接的,所以在连建立socket服务时,就要有服务端存在,并连接成功,形成通路,在该通路上进行数据传输。


服务端:

1、建立服务端的socket服务,ServerSocket.并监听一个端口。

2、获取连接过来的服务端对象。通过serverSocket的accept方法,这是阻塞式方法.

3、客户端如果发送数据,服务端就要使用对应的客户端对象,并获取到该客户端的读取流来读数据。

一个简单的客户端、服务端程序

package Socket;
import java.net.*;
import java.io.*;

public class Client{
	public static void main(String[] args)throws Exception{
	//建立Socket连接,并指定ip和端口
	Socket s=new Socket("127.0.0.1",1000);
	//获得客户端的读写流
	OutputStream out=s.getOutputStream();
	//写入数据
	out.write("TCP Coming".getBytes());
	//关闭客户端
	s.close();
	}
}

package Socket;
import java.net.*;
import java.io.*;

public class Srever {
	public static void main(String[] args)throws Exception{
		//建立服务端socket,并监听一个端口
		ServerSocket ss=new ServerSocket(1000);
		//通过accept方法获得客户端
		Socket s=ss.accept();
		//获得对应客户端读取流
		InputStream in=s.getInputStream();
		byte[] buf=new byte[1024];
		int len=0;
		while((len=in.read(buf))!=-1){
			System.out.println(new String(buf,0,len));
		}
		s.close();
	}
}


练习:

客户端发送数据,服务端接收,反馈数据。

package Socket;
import java.net.*;
import java.io.*;
public class ClientDemo {
	public static void main(String[] args)throws Exception{
		Socket s=new Socket("127.0.0.1",1000);
		//发送数据的输入流
		OutputStream out=s.getOutputStream();
		out.write("我来了".getBytes());
		//读完客户的反馈数据
		InputStream in=s.getInputStream();
		byte[] buf=new byte[1024];
		int len=0;
		len=in.read(buf);
		System.out.println(new String(buf,0,len));
		
		s.close();
	}
}

package Socket;
import java.net.*;
import java.io.*;
public class ServerDemo {
	public static void main(String[] args)throws Exception{
		ServerSocket ss=new ServerSocket(1000);
		//获得客户端
		Socket s=ss.accept();
		//获得读取流
		InputStream in=s.getInputStream();
		byte[] buf=new byte[1024];
		int len=0;
		len=in.read(buf);
		System.out.println(new String(buf,0,len));
		
		
		//获得写入流,输入反馈信息
		OutputStream out=s.getOutputStream();
		out.write("welcome !".getBytes());
		s.close();
	}
}


TCP练习

要求:客户端给服务端发送文本,服务端会将其转换成大写返回给客户端,而且客户端可以不断的进行文本转换,当客户端输入over时。转换结束。


分析:

客户端:源-操作文本---字符流

       目的---网络设备---网络输出流。


步骤:

1、建立客户端,要在控制台上输入数据,要有一个与控制台关联的输入流。

2、为了给服务端发送数据,要有一个客户端的写入流。

3、为了读取服务端的反馈数据,要有一个客户端的读取流。

4、建立服务端,为了读服务端的发来的数据,要有一个对应客户端的读取流

5、服务端要把数据反馈给客户端,因此要有一个对应客户端的输入流。


注意:由于客户端中readLine是阻塞式方法。

package Socket;
import java.net.*;
import java.io.*;

public class Client{
	public static void main(String[] args)throws Exception{
	//建立Socket连接,并指定ip和端口
	Socket s=new Socket("127.0.0.1",1000);
	//获得客户端的读写流
	BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));//控制台的读取
	BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));//网络的读取
	
	PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
	//从控制台上读数据,写数据
	String line=null;
	while((line=bufr.readLine())!=null){//readLine是阻塞式方法,它会一直等待控制台的输入
		if("over".equals(line))
			break;
		pw.println("client:"+line);
		
		String str=br.readLine();//从服务端读大写数据
		System.out.println(str);
	}
	//关闭客户端
	s.close();
	br.close();
	pw.close();
	}
}

package Socket;
import java.net.*;
import java.io.*;

public class Srever {
	public static void main(String[] args)throws Exception{
		//建立服务端socket,并监听一个端口
		ServerSocket ss=new ServerSocket(1000);
		//通过accept方法获得客户端
		Socket s=ss.accept();
		//获得对应客户端读取流
		BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));//读网络数据
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		//读数据,把读到的数据变大写
		String line=null;
		while((line=br.readLine())!=null){
			System.out.println(line);
			pw.println(line.toUpperCase());//把大写数据发送给客户端
		}
		br.close();
		pw.close();
		s.close();
	}
}



练习: 运用tcp协议上传文件

步骤:

客户端:

1、读取流与要复制的文件相关联

2、获得客户端写入流,把文件发送给服务端

3、获取客户端读取流,从服务端反馈的数据


服务端:

1、和一个写入流文件关联,作为目标文件

2、获得客户端读取流,读取客户端发来的数据,将其写到目标文件

3、获得客户端写入流,给客户端反馈数据。


package Socket;
import java.net.*;
import java.io.*;
public class TCPClient {
	public static void main(String[] args)throws Exception{
		Socket s=new Socket("127.0.0.1",1000);
		BufferedReader br=new BufferedReader(new FileReader("e:/TCPClient.java"));
		//上传,写入流
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		String line=null;
		while((line=br.readLine())!=null){
			pw.println(line);
		}
		s.shutdownOutput();//将流至于末尾 ...关闭客户端的输出流
		//读取反馈
		BufferedReader bs=new BufferedReader(new InputStreamReader(s.getInputStream()));
		String str=bs.readLine();
		System.out.println(str);
		
		br.close();
		s.close();
	}
}

package Socket;
import java.net.*;
import java.io.*;
public class TCPServer {
	public static void main(String[] args)throws Exception{
		ServerSocket ss=new ServerSocket(1000);
		Socket s=ss.accept();
		String ip=s.getInetAddress().getHostAddress();
		System.out.println(ip+"...connected");
		
		//读客户端发来的数据
		BufferedReader bs=new BufferedReader(new InputStreamReader(s.getInputStream()));
		PrintWriter p=new PrintWriter(new FileWriter("e:/copy.txt"),true);
		String line=null;
		while((line=bs.readLine())!=null){
			p.println(line);//注意:这里是免刷新的
		}
		//反馈给客户端数据
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		pw.println("上传成功");
		
		s.close();
		
	
	}
}


客户端并发上传图片:

多个客户端对一个服务器同时发送数据,那么把对客户的操作放在多线程的run方法中。


package Socket;

import java.io.*;
import java.net.*;
/*
 * 多个客户端向同一个服务端发送数据。
 * 用多线程方式定义多个客户端,然后把数据的读取操作放在run方法中.
 */
public class BmpClient {
	public static void main(String[] args)throws Exception{
		System.out.println(args[0]);
		if(args[0].length()<1){
			System.out.println("请选择一张图片:");
		}
		//参数作为文件目录
		File file=new File(args[0]);
		if(!(file.exists()&&file.isFile())){
			System.out.println("你所选择的文件有问题,要么不是文件,要么文件不存在");
		}
		if(file.length()>5*1024*1024){
			System.out.println("你选的文件过大,不是图片");
		}
		if(!(file.getName().endsWith(".bmp"))){
			System.out.println("你选的不是.bmp规定格式的文件");
		}
		Socket s=new Socket("127.0.0.1",1000);
		//得到读取流和文件向关联
		InputStream in=new FileInputStream(file);
		//得到客户端写入流
		OutputStream sout=s.getOutputStream();
		//从文件中读取图片
		int len=0;
		byte[] buf=new byte[1024];
		while((in.read(buf))!=-1){
			sout.write(buf);//写入文件
			sout.flush();
		}
		//输出流关闭
		s.shutdownOutput();
		//定义读取流,读服务端反馈数据
		InputStream sin=s.getInputStream();
		byte[] b=new byte[1024];
		int num=sin.read(b);
		System.out.println(new String(b,0,num));
		s.close();
		in.close();
	}
}

package Socket;
import java.io.*;
import java.net.*;
public class BmpServer {
	public static void main(String[] args)throws Exception{
		ServerSocket ss=new ServerSocket(1000);
		//连接多个客户端
		while(true){
			//一个服务端和多个客户端连接
			Socket s=ss.accept();
			new Thread(new PICServer(s)).start();
		}
		
	}
}
class PICServer implements Runnable{
	Socket s;
	OutputStream out=null;
	String ip;
	public PICServer(Socket s){
		this.s=s;
	}
	public void run() {
		int count=0;
		try{
		//IP地址
		ip=s.getInetAddress().getHostAddress();
		//目的文件路径
		File file=new File("e:/"+ip+"("+count+")"+".bmp");
		while(file.exists()){
			file=new File("e:/"+ip+"("+(count++)+")"+".bmp");
		}
		//定义一个写入流和目标文件关联
		out=new FileOutputStream(file);
		//得到客户端读取流
		InputStream sin=s.getInputStream();
		int len=0;
		byte[] buf=new byte[1024];
		while((len=sin.read(buf))!=-1){
			out.write(buf);
			out.flush();
		}
		//给客户端反馈数据
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		pw.write("上传成功");
		pw.flush();
		
		s.close();
		}catch(Exception e){
			System.out.println(ip+"上传文件失败");
		}finally{
			try{
				out.close();
			}catch(Exception e){
				e.printStackTrace();
				
			}
		}
		
	}
	
}

运行结果:




客户端并发登录:

要求:只运行3次登录机会,如果该用户还是不存在,那么久不能继续登录了。

package Socket;
import java.io.*;
import java.net.*;
/*
 * 多个客户端进行用户登录,一个用户只有3次机会进行登录
 * 从一个文件从当数据库,校验用户名
 * 多线程来实现多用户登录。
 */
public class LoginClient {
	public static void main(String[] args)throws Exception{
		Socket s=new Socket("127.0.0.1",1000);
		//控制台输入用户名
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		//获得客户端的写入流
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		//有3次机会可以登录
		for(int i=0;i<3;i++){
			System.out.println("请输入用户名:");
			String name=br.readLine();
			if(name.equals(null)){
				System.out.println("请输入用户名");
			}
			//把用户名发送到服务端去
			pw.println(name);
			//读取服务端反馈
			BufferedReader sb=new BufferedReader(new InputStreamReader(s.getInputStream()));
			String line=sb.readLine();
			System.out.println(line);
			if(line.contains("欢迎")){
				break;//已近登录的用户,不用3次机会了
			}
		}
		s.close();
		br.close();
	}

}

package Socket;

import java.io.*;
import java.net.*;

public class LoginServer {
	public static void main(String[] args) throws Exception {
		ServerSocket ss = new ServerSocket(1000);
		while (true) {
			Socket s = ss.accept();
			new Thread(new User(s)).start();
		}
	}
}

class User implements Runnable {
	Socket s;
	private boolean flag = false;

	public User(Socket s) {
		this.s = s;
	}

	public void run() {
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip + "..connected");
		try {
			for (int x = 0; x < 3; x++) {
			// 客户端的读取流
			BufferedReader sbr = new BufferedReader(new InputStreamReader(
					s.getInputStream()));
			// 和用户明文件关联
			BufferedReader br = new BufferedReader(new InputStreamReader(
					new FileInputStream("e:/user.txt")));
				// 进行校验
				String line = sbr.readLine();
				System.out.println(line + "......");
				String name = null;
				while ((name = br.readLine()) != null) {
					if (name.equals(line)) {
						flag = true;
						break;
					}
				}
				// 反馈给服务端信息
				PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
				if (flag) {
					pw.println(line + "欢迎登录");
					break;
				} else {
					pw.println(line + "用户名不存在");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

User.txt

黑马程序员————网络Socket编程_第3张图片


运行结果:

黑马程序员————网络Socket编程_第4张图片



自定义浏览器:

浏览器都会给服务器发送请求消息头,因此自定义的浏览器也要给服务器发送那些数据。

请求头:

黑马程序员————网络Socket编程_第5张图片


代码:

package Socket;
import java.io.*;
import java.net.*;
public class MyIE {
	public static void main(String[] args)throws Exception{
		Socket s=new Socket("127.0.0.1",8080);
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
		pw.println("GET / HTTP/1.1");
		pw.println("Accept: */*");
		pw.println("Accept-Language: zh-CN,zh;q=0.8");
		pw.println("Host: localhost:11000");
		pw.println("Connection: keep-alive");
		
		pw.println();
		
		BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line=null;
		while((line=br.readLine())!=null){
			System.out.println(line);
		}
		s.close();
	}
}

浏览器运行完毕后,也会得到服务器反馈的消息体。

黑马程序员————网络Socket编程_第6张图片



你可能感兴趣的:(java基础学习笔记)