网络编程:OSI参考模型 TCP/IP参考模型
网络通讯要素:IP地址 端口号 传输协议
IP地址:网络中设备的标识,不易记忆,可用主机名,本机回环地址127.0.0.1,主机名:localhost
端口号:用于标识进程的逻辑地址,不同进程的标识,有效端口0~65535,其中0~1024系统使用或保留端口
传输协议:通讯的规则,常见协议有TCP UDP
InetAddress类:
//获取本机ip对象
InetAddress ip = InetAddress.getLocalHost();
//获取主机名称
System.out.println(ip.getHostName());
//获取主机地址
System.out.println(ip.getHostAddress());
//将指定的字符串主机地址获取其ip对象
InetAddress ip1 = InetAddress.getByName("192.122.1.123");
InetAddress.getByName//获取该对象的所有IP地址
UDP协议:
将数据及源和目的封装成数据包中,不需要建立连接,每个数据包的大小在限制在64K内,因无连接,是不可靠协议,不需要建立连接,速度快
TCP协议:
建立连接,形成传输数据的通道,在连接中进行大量数据传输,通过三次握手完成连接,是可靠协议,必须建立连接,效率会稍低。
Socket:就是为网络服务提供的一种机制,通信的两端都有Socket,网络通信其实就是Socket间的通信,数据在两个Socket间是通过IO传输
例1,需求:将数据通过udp协议发送出去。
步骤:
1,建立udpsocket服务。DatagramSocket 对象。
2,将数据封装成数据包。
3,通过socket服务的send方法将数据包发送出去。
4,关闭服务。
代码:
//创建udpsocket服务。
DatagramSocket ds = new DatagramSocket(8888);
byte[] buf = "hi,ge men 坐着 UDP 来了".getBytes();
//将数据封装成数据包。
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.249"),10000);
//使用socket服务的send方法。将数据发出。
ds.send(dp);
//关闭服务。
ds.close();
例2,需求:建立udpsocket服务,监听一个10000端口,接收数据,并打印到屏幕上。
步骤:
1,建立接收端的socket服务。通常需要监听一个端口。其实就是分配了具体指定的数据标识。
2,通过服务的receive方法接收数据,并将数据存储到数据包中。
3,通过数据包对象的方法,获取具体的数据内容。
4,将数据内容打印。
5,关闭服务.
代码:
//通过该服务接收10000端口来数据。首先要建立一个数据包,用来存储接收到的数据。
DatagramSocket ds = new DatagramSocket(10000);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);//将数据存储到数据包中。该方法是阻塞式方法。
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+"..."+text);
//关闭服务。
ds.close();
例3,一个聊天程序
import java.awt.*;
import java.awt.event.*;
import java.net.*;
public class Chat {
Frame f = new Frame("我的聊天室");
// 定义一个主框架
TextField tfIP = new TextField(15);
// 定义一个输入文本框,指定文本框中最多显示的字符个数为15
List lst = new List(10);
// 定义一个图形化的列表框,表明最多显示10行,多的用滚动条显示
DatagramSocket ds;//数据报套接字是包投递服务的发送或接收点。
//此程序是在一台电脑上,所以可以只定义一个数据报套接字,接、收都用它
/*
* 由于DatagramSocket的构造函数声明可能抛出异常,我们的程序需要用try…catch语句
* 进行异常捕获处理,所以不能直接在这里调用DatagramSocket的构造函数对ds进行初始化,
* 我们需要将ds的初始化放在Chat类的构造函数中去完成。
*/
public Chat() {//构造方法中生成线程,并充当数据接受端
try {
ds = new DatagramSocket(3000);
} catch (Exception ex) {
ex.printStackTrace();
}
//接受方应该在一个单独的线程中运行,一面在接受不到进行阻塞时
//不影响其他程序的运行
new Thread(new Runnable() {//用匿名内部类来实现
public void run() {
byte buf[] = new byte[1024];//构造存放接受内容的数据包的缓冲数组
DatagramPacket dp = new DatagramPacket(buf, 1024);
while (true) {
try {
ds.receive(dp);
//接受网络发送过来的消息,并把接受到的消息放在dp数据包中
lst.add(dp.getAddress().getHostAddress()+":"+dp.getPort()+
": "+new String(buf, 0, dp.getLength()), 0);
//用列表框的add方法把接受到的内容显示出来,第二个参数为0表示把
//最后添加的显示在第一行这样聊天时新的内容才在最上面,不用拖动
//滚动条来显示
//显示时把发送方的ip地址和端口字符串形式先显示,后面显示内容
} catch (Exception e) {
if(!ds.isClosed()){
e.printStackTrace();
//我们在关闭窗口时关闭了数据报套接字,而recevie还在等待,就会有异常
}
}
}
}
}).start();
}
public static void main(String[] args) {
Chat chat = new Chat();
chat.init();
}
// 初始窗口
public void init() {
f.setSize(302, 300);// 设置窗口宽度为300,高度为300
f.add(lst);// 主框架中添加列表框
Panel p = new Panel();
p.setLayout(new BorderLayout());
p.add("West", tfIP);// 把文本框添加到面板上的左边
TextField tfData = new TextField(20);
p.add("East", tfData);// 添加一个可以显示20个字符的文本框放在面板的右边
f.add("South", p); // 把面板添加到主框架的下方
f.setVisible(true); // 让主框架可视
f.setResizable(false);// 让重设窗口大小为false,限制用户改变窗口的大小
// 增加关闭窗口的事件处理代码
f.addWindowListener(new WindowAdapter() {
// 用匿名内部类的方式在主框架上添加一个事件处理器
// 重写关闭方法,让程序退出时释放端口等资源
public void windowClosing(WindowEvent e) {
ds.close(); // 程序退出时,关闭Socket,释放相关资源
f.setVisible(false);
f.dispose();
System.exit(0);
}
});
// 下面增加在消息文本框中按下回车键的事件处理代码
tfData.addActionListener(new ActionListener() {
// 重写actinonPerformed方法,所在组件发生操作时调用,这里处理回车事件
public void actionPerformed(ActionEvent e) {
// 取出文本框中的消息字符串,并将其转换成字节数组
byte[] buf;
//buf=tfData.getText().getBytes();
//如果用此句,tfData变量就必须是final常量
buf = e.getActionCommand().getBytes();
//getActionCommand返回与此动作相关的命令字符串。
try {
DatagramPacket dp = new DatagramPacket(buf, buf.length,
InetAddress.getByName(tfIP.getText()), 3000);
// 新建数据包,通过文本框tfIP文本区域内容得到IP,并转换成为InetAddress类型
// 设定接受端接受该数据包的端口为3000
ds.send(dp); // 发送数据包
} catch (Exception ex) {
ex.printStackTrace();
}
/*
* 上面的Exception的引用变量名不能为e,而是改写成了ex,因为e已经在
* actionPerformed方法中作为形式参数变量名被定义过了。
*/
((TextField) e.getSource()).setText("");
}
});
}
}
TCP网络程序:
客户端对Socket,服务端对象ServerSocket
建立客户端和服务器端,
建立连接后,通过Socket中的IO流进行数据的传输
关闭socket
例2,需求:建立一个客户端,给服务端发送一些文字信息。
步骤:
一,建立客户端:
1,创建客户端的socket服务,通常可以在建立客户端对象时,指定服务端的地址和端口。
如果没有指定,可以通过方法,connect进行指定目的的连接。
2,获取socket流中的输出流对象,给服务端写点数据
3,关闭资源
二,建立服务端:
1,建立服务端socket服务。ServerSocket 给服务端加上一个数字标识,分配一个端口号。
让该程序监听一个端口。
2,获取连接过来的客户端对象。使用获取到的客户端和客户端进行通信。
3,关闭客户端。
4,关闭服务端。
客户端代码:
Socket s = new Socket("192.168.1.249",10002);
//如果连接建立成功,就有了socket流。
//获取socket输出流,将数据写到服务端去。
OutputStream out = s.getOutputStream();
out.write("hi,哥们 骑着 TCP 来了".getBytes());
s.close();
服务端代码:
//建立服务端socket服务,并监听一个端口。
ServerSocket ss = new ServerSocket(10002);
//获取客户端对象。
Socket s = ss.accept();//是阻塞式方法。
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String ip = s.getInetAddress().getHostAddress();
String text = new String(buf,0,len);
System.out.println(ip+":"+text);
s.close();
ss.close();