Java网络编程


通讯原理

1)找到对方IP

2)数据发送到对方指定的应用程序上。为了标识这些应用程序,所以给这些网络应用程序都用数字标识。为了方便称呼这些数字,叫做端口,逻辑端口。

3)定义通信规则,这个通讯规则称为协议。W3C组织定义了通用协议TCP/IP。


网络参考模型


我们使用网络编程,主要关注网络层和传输层。

网络参考模型中的每一层都有相对应的协议:

1)网络层主要使用IP协议

2)传输层主要使用TCP、UDP协议

3)应用层使用的是应用层协议,例如HTTTP协议


IP地址对应对象InetAddress

常用方法

static InetAddress getLocalHost(); 获得本机的IP地址对象

static InetAddress getByName(ip地址或者完整域名); 通过域名或者IP地址字符串获得IP地址对象

 

String getHostName(); 获得IP地址对象对应的主机名

如果没有找到IP地址和主机名的对应关系,则返回IP地址字符串

String getHostAddress(); 获得IP地址对象的IP地址字符串


UDP

特点:

1)将数据及源和目的封装成数据包,不需要连接

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

3)因为无连接,所以是不可靠的协议

4)不需要建立连接,速度快

例如:QQ聊天、视频会议、桌面共享

TCP

特点:

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

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

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

4)必须建立连接,效率会稍低

例如:下载数据


Socket编程

Socket就是为网络服务提供的一种机制。

通信两端都有Socket。

网络编程说的就是Socket编程。

数据在两个Socket间进行IO传输。


UDP传输

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

思路:

1)建立udp socket服务

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

3)通过socket服务的发送功能,将数据发送出去

4)关闭资源

程序代码:

public class Udpdemo1 {
	public static void main(String[] args) throws IOException {
		//创建udp服务,通过DatagramSocket对象
		DatagramSocket ds = new DatagramSocket();
		//确定要发送的数据,并封装成数据包,DatagramPacket对象
		byte[] buf = "hello, i'm coming".getBytes();
		DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("localhost"), 10000);
		//通过socket服务,将已有的数据包发送出去,通过send(datagramPacket)
		ds.send(dp);
		//关闭资源
		ds.close();
	}
}


需求:

定义一个应用程序,用于接收udp协议出生的数据并处理。

思路:

1)定义udp Socket服务

2)定义一个数据包用于存储接收到的字节数据。因为数据包对象有更多功能可以提取字节数据中的不同数据信息。

3)通过Socket的receive方法将接收的数据存入已定义好的数据包中。

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

5)关闭资源
程序代码:

<span style="font-size:14px;">class UdpReceive{
	public static void main(String[] args) throws IOException {
		//创建udp socket服务,绑定监听端口号
		DatagramSocket ds = new  DatagramSocket(10000);
		//定义数据包,用于存储数据
		byte[] buf = new byte[1024];
		DatagramPacket dp = new DatagramPacket(buf, buf.length);
		//通过服务的receive方法将数据存入数据包中
		ds.receive(dp);
		//通过数据包的方法获取其中的数据
		String ip = dp.getAddress().getHostAddress();
		String data = new String(dp.getData(), 0, dp.getLength());
		//关闭资源
		ds.close();
	}
}</span>


编写一个聊天程序

1)有接收数据的部分和发数据的部分

2)这两部分需要同时执行,需要用到多线程技术,一个线程控制收,一个线程控制发

因为收和发动作不一样,所以要定义两个run方法

发送端线程

class UdpChatSend implements Runnable{
	private DatagramSocket ds;
	public UdpChatSend(DatagramSocket ds){
		this.ds = ds;
	}
	@Override
	public void run() {
		//键盘录入
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String line = null;
		try {
			while((line=br.readLine())!=null){
				if(line.equals("886"))//定义结束标记
					break;
				byte[] buf = line.getBytes();
				DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("localhost"), 11000);		
				ds.send(dp);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		ds.close();
	}
}


接收端线程

class UdpChatReceive implements Runnable{
	private DatagramSocket ds;
	public UdpChatReceive(DatagramSocket ds){
		this.ds = ds;
	}
	@Override
	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());
				System.out.println(ip+"---"+data);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		ds.close();
	}
}


主程序代码

public class UdpChatDemo {
	public static void main(String[] args) {
		
		try {
			DatagramSocket dSend = new DatagramSocket();
			DatagramSocket dReceive = new DatagramSocket(11000);
			new Thread(new UdpChatSend(dSend)).start();
			new Thread(new UdpChatReceive(dReceive)).start();
		} catch (SocketException e) {
			e.printStackTrace();
		}
		
	}

}

运行截图


如果对方也有发送端和接收端,就可以进行聊天


TCP传输

1)TCP分客户端和服务端

2)客户端对应的对象是Socket,服务端对应的对象是ServerSocket

客户端

Socket对象在建立时,就可以去连接指定主机。

因为tcp面向连接,所以在建立socket服务时,就要有服务端存在,并连接成功。

形成通路后,在该通道进行数据的传输。

步骤:

1)创建Socket服务,并指定要连接的主机和端口。

2)获得Socket输出流

3)向输出流中写入数据
4)关闭客户端

程序代码:

public class TcpDemoSend1 {
	public static void main(String[] args) throws IOException {
		//创建Socket服务,并指定要连接的主机和端口。
		Socket s = new Socket("192.168.1.254", 10000);
		//获得Socket输出流,以便向流中写入要发送的数据
		OutputStream os = s.getOutputStream();
		//向输出流中写入数据
		os.write("hello, 服务端".getBytes());
		//关闭客户端
		s.close();
	}
}

服务端

需求:定义端点接收数据并打印在控制台上

步骤:

1)建立服务端的Socket服务,通过ServerSocket对象,并监听一个端口

2)获取连接过来的客户端对象,通过ServerSocket的accept方法,该方法是一个阻塞式方法,没有连接就会等,

3)客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流读取发过来的数据,并打印在控制台上。

4)关闭客户端连接

5)关闭服务端(可选)

程序代码:

<pre name="code" class="java">class TcpDemoReceive{
	public static void main(String[] args) throws IOException {
		//建立服务端的Socket服务,并监听一个端口
		ServerSocket ss = new ServerSocket(10000);
		//通过accept方法获取连接过来的客户端对象
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"----connected");
		//获取客户端发送过来的数据,那么要使用客户端对象的读入流来读取数据
		InputStream is = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = is.read(buf);
		System.out.println(new String(buf, 0, len));
		//关闭客户端
		s.close();
		//关闭服务端(可选)
		ss.close();
	}
}


TCP传输中客户端和服务端的互访

需求:客户端给服务端发送数据,服务端收到后,给客户端发送发送反馈信息。

客户端:

1)建立Socket服务,指定连接主机和端口

2)获取Socket输出流,将数据写入流中,通过网络服务发送到服务端

3)获取Socket输入流,将服务器的反馈数据读取出来

4)关闭资源

程序代码:

客户端

public class TcpDemoSend2 {
	public static void main(String[] args) throws IOException {
		Socket s = new Socket("localhost", 10000);
		OutputStream os = s.getOutputStream();
		os.write("你好,服务端".getBytes());
		InputStream is = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = is.read(buf);
		System.out.println(new String(buf, 0, len));
		s.close();
	}

}
服务端

class TcpDemoReceive2{
	public static void main(String[] args) throws IOException {
		ServerSocket ss = new ServerSocket(10000);
		Socket s = ss.accept();
		InputStream is = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = is.read(buf);
		System.out.println(new String(buf, 0, len));
		OutputStream os = s.getOutputStream();
		os.write("你好,客户端".getBytes());
		s.close();
		ss.close();
	}
}
运行截图:

客户端                                                                                                      服务端
      
练习:

需求:建立一个文本转换器

客户端给服务器发送文本,服务端会将文本转换成大写再返回给客户端,客户端可以不端的进行文本转换,当客户端输入over时,转换结束。

程序代码:

客户端

public class TcpDemoSend3 {
	public static void main(String[] args) throws IOException {
		Socket s = new Socket("localhost", 10000);
		BufferedReader brKeyboard = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
		BufferedReader brSocket = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		while((line=brKeyboard.readLine())!=null){
			if(line.equals("over"))
				break;
			//将数据写入socket输出流中,以换行作为分隔符,并且在写入数据后刷新
			pw.println(line);
			//读取返回信息并打印
			System.out.println(brSocket.readLine());
		}		
		s.close();
	}

}
服务端

class TcpDemoReceive3{
	public static void main(String[] args) throws IOException {
		ServerSocket ss = new ServerSocket(10000);
		Socket s = ss.accept();
		BufferedReader brSocket = new BufferedReader(new InputStreamReader(s.getInputStream()));
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
		String line = null;
		while((line=brSocket.readLine())!=null){
			pw.println(line.toUpperCase());
		}
		s.close();
		ss.close();
	}
}
客户端运行截图:



练习2:上传图片

程序代码:

客户端

public class PicUploadClient {
	public static void main(String[] args) throws IOException {
		Socket s = new Socket("localhost", 10000);
		InputStream fileIs = new FileInputStream("e:/1.jpg");
		OutputStream os = s.getOutputStream();
		InputStream is = s.getInputStream();
		int len = 0;
		byte[] buf = new byte[1024];
		while((len=fileIs.read(buf))!=-1){
			os.write(buf, 0, len);
		}
		//禁用此套接字的输出流,添加结束标记,调用该方法后不能再像输出流中写入数据
		s.shutdownOutput();
		os.write("hehe".getBytes());
		len = is.read(buf);
		System.out.println(new String(buf, 0, len));
		s.close();
	}

}
服务端

class PicUploadServer{
	public static void main(String[] args) throws IOException {
		ServerSocket ss = new ServerSocket(10000);
		Socket s = ss.accept();
		OutputStream os = s.getOutputStream();
		InputStream is = s.getInputStream();
		OutputStream fileOs = new FileOutputStream("e:/copy.jpg");
		byte[] buf = new byte[1024];
		int len = 0;
		while((len=is.read(buf))!=-1){
			fileOs.write(buf, 0, len);
		}
		os.write("上传成功".getBytes());
		s.close();
		ss.close();
	}
}
并发上传图片:

服务端

单线程的服务端有局限性。当A客户端连接上以后,被服务器获取到,服务端执行具体流程。这时B客户端连接,只有等待。

因为服务端还没有处理完A客户端的请求,还没有循环回来执行下accept方法。所以暂时获取不到B客户端对象。

那么为了可以让多个客户端同时并发访问服务端,那么服务端最后就是让每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求。

程序代码:

服务端

public class PicUploadThread {
	public static void main(String[] args) throws IOException {
		ServerSocket ss = new ServerSocket(10000);
		while(true){
			Socket s = ss.accept();
			new Thread(new PicUploadThreadServer(s)).start();
		}
	}

}
class PicUploadThreadServer implements Runnable{
	private Socket s;
	public PicUploadThreadServer(Socket s){
		this.s = s;
	}
	@Override
	public void run() {
		try {
			int count = 0;
			OutputStream os = s.getOutputStream();
			InputStream is = s.getInputStream();
			String ip = s.getInetAddress().getHostAddress();
			System.out.println(ip+"----connected");
			File file = new File("e:"+File.separator+ip+"("+count+").jpg");
			if(file.exists()){
				count++;
			}
			OutputStream fileOs = new FileOutputStream(file);
			byte[] buf = new byte[1024];
			int len = 0;
			while((len=is.read(buf))!=-1){
				fileOs.write(buf, 0, len);
			}
			os.write("上传成功".getBytes());
			fileOs.close();
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}


客户端

class PicUploadClient2{
	public static void main(String[] args) throws IOException {
		if(args.length!=1){
			System.out.println("请选择一个jpg格式的图片");
			return;
		}
		File file = new File(args[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("localhost", 10000);
		System.out.println(file);
		InputStream fileIs = new FileInputStream(file);
		OutputStream os = s.getOutputStream();
		InputStream is = s.getInputStream();
		int len = 0;
		byte[] buf = new byte[1024];
		while((len=fileIs.read(buf))!=-1){
			os.write(buf, 0, len);
		}
		//禁用此套接字的输出流,添加结束标记,调用该方法后不能再像输出流中写入数据
		s.shutdownOutput();
		len = is.read(buf);
		System.out.println(new String(buf, 0, len));
		fileIs.close();
		s.close();
	}
}
练习:并发登录,验证三次

服务端

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

}

class LoginThreadServer implements  Runnable{
	private Socket s;
	public LoginThreadServer(Socket s){
		this.s = s;
	}
	@Override
	public void run() {
		try {
			String ip = s.getInetAddress().getHostAddress();
			System.out.println(ip+"----connected");
			BufferedReader brSocket = new BufferedReader(new InputStreamReader(s.getInputStream()));
			PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
			
			for(int i=0; i<3; i++){
				BufferedReader br = new BufferedReader(new FileReader("e:/database.txt"));
				String line = null;
				System.out.println("in");
				String name = brSocket.readLine();
				boolean flag = false;
				System.out.println(name);
				while((line=br.readLine())!=null){
					if(line.equals(name)){
						flag = true;
						break;
					}
				}
				if(flag){
					System.out.println(name+",已登录");
					pw.println(name+",登录成功");
					break;
				}else{
					System.out.println(name+"正在尝试登录");
					pw.println(name+",用户名不存在");
				}
				br.close();
			}
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


客户端

class LoginClient {
	public static void main(String[] args) throws IOException {
		Socket s = new Socket("localhost", 10000);
		BufferedReader brKeyboard = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
		BufferedReader brSocket = new BufferedReader(new InputStreamReader(s.getInputStream()));
		String line = null;
		while((line=brKeyboard.readLine())!=null){
			//将数据写入socket输出流中,以换行作为分隔符,并且在写入数据后刷新
			pw.println(line);
			//读取返回信息并打印
			System.out.println(brSocket.readLine());
		}	
		for(int i=0; i<3; i++){
			line=brKeyboard.readLine();
			if(line==null)
				break;
			pw.println(line);
			//读取返回信息并打印
			System.out.println(brSocket.readLine());
		}
		brKeyboard.close();
		s.close();
	}

}


应用层
URL

统一资源定位符

构造方法

new URL(str);

new URL(protocol, host, port, file);

常用方法

String getProtocol();  获得协议

String getHost();获得主机

String getPort();获得端口号

String getFile();获得带参数的文件路径

String getPath();获得文件路径

String getQuery();获得参数部分

示例代码:

public class URLDemo {
	public static void main(String[] args) throws MalformedURLException {
		URL url = new URL("http://localhost:8080/myweb/demo.html?name=haha&age=12");
		System.out.println("getProtocol :"+url.getProtocol());
		System.out.println("getHost :"+url.getHost());
		System.out.println("getPort :"+url.getPort());
		System.out.println("getFile :"+url.getFile());
		System.out.println("getPath :"+url.getPath());
		System.out.println("getQuery :"+url.getQuery());
	}

}
运行截图:



URLConnection openConnection();  返回一个URLConnection 对象,它表示到URL 所引用的远程对象的连接。 

InputStream getInputStream();   获得输入流,该输入流中的数据是应用层中的数据

public class URLDemo2 {
	public static void main(String[] args) throws IOException {
		URL url = new URL("http://localhost:8080/myweb/demo.html?name=haha&age=12");
		URLConnection urlconn = url.openConnection();
		InputStream is = urlconn.getInputStream();
		byte[] buf = new byte[1024];
		int len = is.read(buf);
		System.out.println(new String(buf, 0, len));
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值