黑马程序员--Java基础之网络编程

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流!


Java之网络编程整理


概述:


端口
 web服务:80
 web服务器:8080
 MySQL:3306


网络传输的三要素:IP 端口 协议


网络通讯要素.jpg


java.net包中的InetAddress类
没有构造方法
方法:
    String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。 
    String getHostName() 获取此 IP 地址的主机名。 
    static InetAddress getLocalHost() 返回本地主机。 
   
    static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。 
Demo:
import java.net.*;
class IPDemo 
{
public static void main(String[] args) throws Exception
{
//InetAddress i = InetAddress.getLocalHost();


//System.out.println(i.toString());//2013-20140526XY/192.168.1.107
//System.out.println("address:"+i.getHostAddress());//address:192.168.1.107
//System.out.println("name:"+i.getHostName());///name:2013-20140526XY


InetAddress ia = InetAddress.getByName("192.168.1.108");//常用
System.out.println("address:"+ia.getHostAddress());//address:192.168.1.108
System.out.println("name:"+ia.getHostName());//name:192.168.1.108
// 如果IP地址和它对应的主机名没有在网络上,返回的主机名还是IP地址


InetAddress ia2 = InetAddress.getByName("www.baidu.com");
System.out.println("address:"+ia2.getHostAddress());//address:119.75.217.56 如果IP地址不止一个,应该用static InetAddress[] getAllByName(String host) 
  //在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。 
System.out.println("name:"+ia2.getHostName());//name:www.baidu.com
}
}


一、UDP传输
DatagramSocket与DatagramPacket
建立发送端和接收端
建立数据包
调用Socket的发送接收方法
关闭Socket


发送端与接收端是两个独立的运行程序


玩Socket主要就是记住流程,先干什么后干什么,代码可以查文档




Demo:
//两个窗口。一个发送一个接收
import java.net.*;
/*
需求:通过UDP传输方式,将一段文字数据发送出去


定义udp的发送端
思路:
1.建立udpsocket服务
2.提供数据,并将数据封装到数据包中
3.通过Socket服务的发送功能,将数据包发送出去
4.关闭资源


*/


class  UdpSend
{
public static void main(String[] args) throws Exception
{
//1.通过DatagramSocket对象创建UDP服务
DatagramSocket ds = new DatagramSocket();


//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.107"),10000);


//3.通过Socket服务,将已有的数据包发送出去,通过send方法
ds.send(dp);


//4.关闭资源
ds.close();
}
}


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


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


class UdpReceive
{
public static void main(String[] args)throws Exception
{
//1.创建udpsocket服务,建立端点
DatagramSocket ds = new DatagramSocket(10000);//监听10000端口


//2.定义一个数据包,用于存储数据 DatagramPacket(byte[] buf, int length)
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);


//3.通过Socket服务的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);//接收到的数据打印结果为:192.168.1.107::udp ge men lai le::63473
//随机给发送端分配了一个端口63473。也可以在发送端指定端口


//5.关闭资源
ds.close();
}
}


二、UDP 键盘录入方式


java.net.BindException 端口已经被使用就会出现此异常


Demo:
/*
两个窗口实现键盘录入数据的发送和接收(两个进程)
*/
import java.io.*;
import java.net.*;


//发送键盘录入的数据
class UdpSend2
{
public static void main(String[] args) throws Exception
{
DatagramSocket ds = new DatagramSocket();


BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line=bufr.readLine())!=null)
{
if("886".equals(line))
break;
byte[] buf = line.getBytes();


DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.107"),10001);
ds.send(dp);
}
ds.close();
}
}


class UdpReceive2
{
public static void main(String[] agrs)throws Exception
{
DatagramSocket ds = new DatagramSocket(10001);


while(true)
{
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);


ds.receive(dp);


String ip = dp.getAddress().getHostName();
String data = new String(dp.getData(),0,dp.getLength());


System.out.println(ip+"::"+data);
}
}
}


三、UDP 聊天(掌握)
用一个窗口实现发送接收(多线程)


/*
编写一个聊天程序:有收数据的部分和发数据的部分
这两部分需要同时执行
那就需要用到多线程技术:一个线程控制收,一个线程控制发


因为收和发动作是不一致的,所以要定义两个run方法
而且这两个方法要封装到不同的类中
*/
import java.net.*;
import java.io.*;
//发送端
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)
{
if("886".equals(line))
break;
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.107"),1003);
ds.send(dp);
}
}
catch (Exception e)
{
throw new RuntimeException("发送端失败");
}
}
}


//接收端
class Receive implements Runnable
{
private DatagramSocket ds;
public Receive(DatagramSocket ds)
{
this.ds = ds;
}


public void run()
{
try
{
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 (Exception e)
{
throw new RuntimeException("接收端失败");
}
}
}


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


new Thread(new Send(sendSocket)).start();
new Thread(new Receive(receiveSocket)).start();
}
}


四、TCP传输
Socket和ServerSocket
建立客户端和服务器端
建立连接后,通过Socket中的IO流进行数据的传输
关闭Socket
同样,客户端与服务器端是两个独立的应用程序
TCP原理.jpg


Socket类
构造方法:
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
方法:
InputStream getInputStream()  返回此套接字的输入流。 
OutputStream getOutputStream() 返回此套接字的输出流。 




Demo1:客户端发送数据给服务端
/*
演示TCP传输


1.tcp分客户端和服务端
2.客户端对应的对象是Socket
 服务端对应的对象是ServerSocket
*/


/*
客户端:
通过查阅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.107",10003);


//为了发送数据,应该获取Socket流中的输出流
OutputStream out = s.getOutputStream();
out.write("tcp ge men lai le".getBytes());


s.close();


}
}


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


服务端:
1.建立服务端的Socket服务,ServerSocket();
 并监听一个端口
2.获取连接过来的客户端对象
  通过ServerSocket的accept方法,没有连接就会等,所以这个方法是阻塞式的
3.客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据
4.关闭服务端(可选)
*/
class TcpServer
{
public static void main(String[] args)throws Exception
{
//建立服务端的Socket服务,ServerSocket();
ServerSocket ss = new ServerSocket(10003);


//通过ServerSocket的accept方法获取连接过来的客户端对象
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();//关闭客户端


}
}








Demo2:Demo1:客户端发送数据给服务端,服务端给反馈


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


/*
演示TCP传输的客户端和服务端的互访


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


/*
客户端:
1.建立Socket服务,指定要连接的主机和端口
2.获取Socket流中的输出流,将数据写到该流中。通过网络发送给服务端
3.获取Socket流中的输入流,将服务端反馈的数据获取到,并打印
4.关闭客户端资源
*/
class TcpClient2
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("192.168.1.107",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();
out.write("哥们收到,你也好".getBytes());


s.close();
ss.close();
}
}






Test:(有点问题,连接不能建立)


/*
需求:建立一个文本转换服务器
客户端给服务器端发送文本,服务端会将文本转换成大写再返回给客户
而且客户端可以不断额进行文本转换,当客户端输入over时,转换结束


分析:
客户端:
既然是操作设备上的数据,那么就可以使用IO技术,并按照IO的操作规律来思考
源:键盘录入
目的:网络设备,网络输出流
而且操作的是文本数据,可以选择字符流


步骤:
1.建立服务
2.获取键盘录入
3.将数据发送给服务端
4.获取服务端返回的大写数据
5.结束,关资源


都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
*/
import java.net.*;
import java.io.*;


class  TransClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("192.168.1.107",10005);


//定义源
//定义读取键盘数据的流对象
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

//定义目的,将数据写入到Socket输出流,发给服务端
BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));//目的是Socket输出流
//可简写为
//PrintWriter out = new PrintWriter();


//定义一个Socket读取流,读取服务端返回的大写信息
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));


String line = null;
while ((line = bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufOut.write(line);
bufOut.newLine();
bufOut.flush();
//上面三行也就可以简写为 out.println(line);


String str = bufIn.readLine();
System.out.println("server:"+str);
}


bufr.close();
s.close();//关闭Socket时会有一个-1的结束标记传给服务端,告诉服务端这边结束。
//当服务端读到-1时就会结束,Socket服务也会关闭
}
}


/*


服务端:
源:Socket读取流
目的:Socket输出流
都是文本,装饰
*/
class  TransServer
{
public static void main(String[] args) throws Exception
{
//建立端点,监听端口
ServerSocket ss = new ServerSocket(10005);


//拿到一个客户端对象
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"......connected");


//读取客户端发来的数据  包装Socket读取流
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));


//目的  读取Socket输出流,将大写写入到Socket输出流,并发送给客户端
BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));


String line = null;
while ((line = bufIn.readLine())!=null)
{
System.out.println(line);
bufOut.write(line.toUpperCase());
bufOut.newLine();//按行读取的,一定要换行
bufOut.flush();


}


s.close();
ss.close();
}
}


/*
该例子出现的问题
现象:客户端和服务端都在莫名的等待
为什么呢?
因为客户端和服务端都有阻塞式方法,这些方法都没有读到结束标记,就会一直等而导致两端都在一直等待
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值