网络编程
概述
计算机网络:
是指将地理位置不同的具有独立功能的多台计算机及外部设备,通过通信线路链接起来,在网络操作系统,网络管理软件及网络通讯协议,的管理和协调下,实现资源共享和信息传递的计算机系统
网络编程:
是指就是用来实现,不同计算机上运行的程序间可以进行数据交换
网络模型
计算机间用那种规则进行通信,就是网络模型研究的问题,网络模型一般指
网络七层概述
1.物理层: 主要定义物理设备的标准,如网线的接口类型,各种传输介质的传输率,他的主要作用是传输比特流,(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
2. 数据链路层:主要将从物理层接收到的数据,进行MAC地址(网卡的地址)的封装和解封装,长把这一层的数据叫做帧,这一层工作的设备叫做交换机,数据通过交换机进行传输
3. 网络层: 主要将从下层接收的数据,进行IP地址的封装和解封装,在这一层工作的设备叫做路由器,长把这一层的数据叫做数据包
4. 传输层: 定义了一些传输数据的协议和端口号。如 TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求较高的,数据量大的数据) UDP(用户数据报协议,与TCP协议恰恰相反,用于传输可靠性要求不高,数据量小的数据),主要是从下层接收到的数据,进行分段和传输,到达目的地址后,在进行重组,常常把这一层的数据叫做段
5.会话层: 通过传输层(端口号:传输端口和接收端口)建立数据传输的通道,主要在你的系统之间发起会话或者接收会话(设备之间需要互相认识,可以通过ip也可以是MAC或者是主机名)
6.表示层: 主要是对接收的数据进行解释,加密与解密,压缩和解压缩,等(也就是吧计算机能看懂的东西,转换成人能够看懂的东西)
7.应用层: 主要是一些终端应用,比如 QQ,浏览器这类的(可以把它理解成我们在电脑屏幕上看到的东西,就是终端应用)
网络编程三要素
发送信件我们需要有三点,才能保证发送信件的成功
1 收件人地址 (ip)
2 收件人(端口)
3 信的内容,双方都可以看懂(协议)
IP
1.IP:计算机在互联网上的唯一地址;
192.168.0.100:四段0--255的数据组成(IPv4):点分十进制
IP地址的范围:
最小IP地址:000.000.000.000:
最大IP地址:255.255.255.255:
A:所谓IP地址就是给每个连接在Internet上的主机分配的一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,
每个IP地址长32bit,比特换算成字节,就是4个字节。
例如一个采用二进制形式的IP地址是“11000000101010000000000101100100”,
这么长的地址,人们处理起来也太费劲了。
为了方便人们的使用,IP地址经常被写成十进制的形式,中间使用符号“.”分开不同的字节。
于是,上面的IP地址可以表示为“10.0.0.1”。IP地址的这种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多。
B:IP地址的组成
IP地址 = 网络号码+主机地址
A类IP地址:第一段号码为网络号码,剩下的三段号码为本地计算机的号码:192.168.0.100:192是网络号码
B类IP地址:前二段号码为网络号码,剩下的二段号码为本地计算机的号码
C类IP地址:前三段号码为网络号码,剩下的一段号码为本地计算机的号码
以下三个IP地址:
1).192.168.0.100;
2).192.168.0.200;
3).192.168.1.100;
请问以上三个IP地址哪些可以互相访问?关键取决于"子网掩码":
如果子网掩码:255.255.255.0
11111111.11111111.11111111.00000000(子网掩码中,全部是1端被标识为"网络号码")
此时:1)和2)可以互相访问;
如果子网掩码:255.255.0.0
11111111.11111111.00000000.00000000
此时:1)和2)和3)都可以互访;
特殊地址:
127.0.0.1 回环地址,可用于测试本机的网络是否有问题. ping 127.0.0.1
DOS命令 ipconfig:查看本机IP地址
xxx.xxx.xxx.0 网络地址
xxx.xxx.xxx.255 广播地址
A类 1.0.0.1---127.255.255.254 (1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址) (2)127.X.X.X是保留地址,用做循环测试用的。
B类 128.0.0.1---191.255.255.254 172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
C类 192.0.0.1---223.255.255.254 192.168.X.X是私有地址
D类 224.0.0.1---239.255.255.254
E类 240.0.0.1---247.255.255.254
端口
1).物理端口:网卡口;
2).逻辑端口:
1>.由操作系统维护的;
2>.Windows的逻辑端口范围:0--65535
3>.其中0--1024一般由系统内部使用,我们尽量不要使用;
4>.一个网络应用程序,至少要占用一个端口,可以同时占用多个端口;
一个端口,在同一时刻,只能由一个应用程序占用;
协议
3.协议:TCP,UDP,FTP,HTTP....
1).UDP协议:
1.数据打包发送;
2.数据包大小有限制,最大64K;
3.面向无连接的(发送信息时,可以没有接收端);
所以不安全的;
类似于:电视信号、广播信号;
2).TCP协议:
1.数据流发送数据;
2.数据大小无限制;
3.面向连接的(发送信息时,必须要有接收方)
类似于:打电话;
三次握手确保链接的建立:1 客户端 请求链接
2 服务端 确定授予
3 客户端 收到
InetAddress
java.net.InetAddress(类):
IP 地址是 IP 使用的 32 位或 128 位无符号数字,它是一种低级协议,UDP 和 TCP 协议都是在它的基础上构建的。
成员方法:
静态方法:
获取任意主机:static InetAddress getByName(String pcName):
获取自己主机: static InetAddress getLocalHost();
普通方法:
主机名:getHostName
主机Ip地址:getHostAddress
InetAddress localHost = InetAddress.getLocalHost();
Log.d("mmm", localHost.getHostName() + "/" + localHost.getHostAddress());
看下Log
09-04 10:41:07.902 11774-11867/com.example.jh.rxhapp D/mmm: localhost/127.0.0.1
Socket
两台计算机如何通信
Socket套接字:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
Socket原理机制:
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。
UDP发送数据
public class UdpClient extends Thread {
/*
* UDP的发送端:
*
* 1.实例化一个DatagramSocket对象;
* 2.准备数据:
* 1).目标IP--InetAddress对象
* 2).目标端口;
* 3).信息内容--byte[]数组
* 3.实例化一个数据包对象:DatagramPacket对象
* 4.调用DatagramSocket对象的方法,将数据包发送出去;
*/
@Override
public void run() {
//实例化一个DatagramSocket对象
DatagramSocket datagramSocket = null;
try {
datagramSocket = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
Log.d("mmm", "服务器连接失败");
}
//准备数据
InetAddress ip = null;
try {
ip = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
Log.d("mmm", "未找到服务器");
}
int port = 8888;
byte[] bytes = "UDP发送的数据,请接收".getBytes();
//实例化DatagramPacket对象
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, ip, port);
//发送数据
try {
datagramSocket.send(datagramPacket);
} catch (IOException e) {
e.printStackTrace();
Log.d("mmm", "消息发送失败");
}
//释放资源
datagramSocket.close();
Log.d("mmm", "发送完毕");
}
}
public class UdpServer extends Thread {
/*
* 接收端:
*
* 1.实例化一个DatagramSocket(端口):
* 2.准备一个空的byte[]数组,用于接收数据;
* 准备一个空的数据包对象:DatagramPacket
* 3.调用DatagramSocket的方法接收数据;
* 4.从DatagramPacket中解析数据;
* 5.释放资源
*/
@Override
public void run() {
try {
//实例化一个DatagramSocket,带端口
DatagramSocket datagramSocket = new DatagramSocket(8888);
//创建一个空的byte数组
byte[] bytes = new byte[1024];
//准备一个空的DatagramPacket
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
Log.d("mmm", "服务端启动等待接收数据");
//接收数据
datagramSocket.receive(datagramPacket);
Log.d("mmm", "服务端接收数据完毕");
//解析数据
String ip = datagramPacket.getAddress().getHostAddress();
byte[] data = datagramPacket.getData();
String s = new String(data, 0, data.length);
Log.d("mmm", "接收到IP:" + ip + "接收到数据:" + s);
//释放资源
datagramSocket.close();
} catch (Exception e) {
e.printStackTrace();
Log.d("mmm", "接收异常");
}
}
}
public class UDPActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_udp);
new UdpServer().start();
findViewById(R.id.udp_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new UdpClient().start();
}
});
}
}
看下log
09-04 11:56:12.062 14425-14546/com.example.jh.rxhapp D/mmm: 服务端启动等待接收数据
09-04 11:56:13.930 14425-14572/com.example.jh.rxhapp D/mmm: 发送完毕
09-04 11:56:13.930 14425-14546/com.example.jh.rxhapp D/mmm: 服务端接收数据完毕
09-04 11:56:13.930 14425-14546/com.example.jh.rxhapp D/mmm: 接收到IP:127.0.0.1接收到数据:UDP发送的数据,请接收
Tcp基本使用
public class TcpClient extends Thread{
/*
* TCP发送端:
*
* 1.实例化一个Socket对象,要指定IP和端口;
* 2.如果要发送数据,通过Socket对象获取输出流:OutputStream
* 3.输出数据;
* 4.释放资源:
*/
@Override
public void run() {
try {
//实例化一个Scoket对象,指定ip和端口
Socket socket = new Socket("127.0.0.1", 8888);
//获取发送数据的输出流
OutputStream outputStream = socket.getOutputStream();
//输出数据
outputStream.write("我是TCP发送数据".getBytes());
//释放资源
socket.close();
Log.d("mmm","客户端发送完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class TcpServer extends Thread {
/*
* TCP接收数据:
*
* 1.实例化一个ServerSocket,并指定端口号;
* 调用ServerSocket的accept()方法等待连接:
* 2.由于要读取数据,所以通过ServerSocket对象获取"输入流"--InputStream
* 3.读取信息;
* 4.释放资源;
*/
@Override
public void run() {
//实例化一个实例化一个ServerSocket,并指定端口
try {
ServerSocket serverSocket = new ServerSocket(8888);
//调用serverSocket的accept方法,等待连接
Log.d("mmm", "等待连接");
Socket socket = serverSocket.accept();
Log.d("mmm", "有用户连接");
//获取输入流读取数据
InputStream inputStream = socket.getInputStream();
//读取信息
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String s = new String(bytes, 0, len);
Log.d("mmm", s);
//释放资源
serverSocket.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
调用
public class TCPActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcp);
new TcpServer().start();
findViewById(R.id.tcp_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new TcpClient().start();
}
});
}
}
看下log
09-08 14:32:03.415 6654-6842/com.example.jh.rxhapp D/mmm: 等待连接
09-08 14:32:06.067 6654-6842/com.example.jh.rxhapp D/mmm: 有用户连接
09-08 14:32:06.068 6654-6842/com.example.jh.rxhapp D/mmm: 我是TCP发送数据
09-08 14:32:06.070 6654-6879/com.example.jh.rxhapp D/mmm: 客户端发送完毕
Tcp实现聊天功能
public class TcpChatClient extends Thread {
private final Handler mHandler;
private OutputStream mOutputStream;
private Socket mSocket;
public TcpChatClient(Handler handler) {
this.mHandler = handler;
}
/*
* TCP发送端:
*
* 1.实例化一个Socket对象,要指定IP和端口;
* 2.如果要发送数据,通过Socket对象获取输出流:OutputStream
* 3.输出数据;
* 4.不断读取数据
*/
@Override
public void run() {
try {
//实例化一个Socket
mSocket = new Socket("127.0.0.1", 8888);
//获取输出流
mOutputStream = mSocket.getOutputStream();
//发送消息
sendMessage("客户端请求链接");
//获取输入流
InputStream inputStream = mSocket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
//不断的读取数据
while (true) {
len = inputStream.read(bytes);
String msg = new String(bytes, 0, len);
Message msg1 = new Message();
msg1.obj = "客户端收到:" + msg;
mHandler.sendMessage(msg1);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 发送消息
*
* @param message
*/
public void sendMessage(String message) {
try {
mOutputStream.write(message.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
private void colseSocket() {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public class TcpChatServer extends Thread {
private final Handler mHanlder;
private Socket mSocket;
private OutputStream mOutputStream;
public TcpChatServer(Handler handler) {
this.mHanlder = handler;
}
/*
* TCP接收数据:
*
* 1.实例化一个ServerSocket,并指定端口号;
* 调用ServerSocket的accept()方法等待连接:
* 2.由于要读取数据,所以通过ServerSocket对象获取"输入流"--InputStream
* 3.读取信息;
* 4.获取输出流,发送消息
*/
@Override
public void run() {
try {
//实例化ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
//等待链接
Log.d("mmm", "服务器等待链接");
mSocket = serverSocket.accept();
Log.d("mmm", "链接成功");
//获取书输入流
InputStream inputStream = mSocket.getInputStream();
//获取输出流
mOutputStream = mSocket.getOutputStream();
//读取消息
byte[] bytes = new byte[1024];
//不断的读取数据
while (true) {
int len = inputStream.read(bytes);
String s = new String(bytes, 0, len);
Message msg = new Message();
msg.obj = "服务端收到:"+s;
mHanlder.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 发送消息
* @param msg
*/
public void sendMessage(String msg) {
try {
mOutputStream.write(msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class TcpChatClientActivity extends AppCompatActivity {
private TextView mTextView;
private EditText mEditText;
private Button mClient;
private Button mServer;
private StringBuffer mStringBuffer;
public Handler handler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
String mssage = (String) msg.obj;
mStringBuffer.append("\n" + mssage);
mTextView.setText(mStringBuffer);
}
};
private TcpChatServer mTcpChatServer;
private TcpChatClient mTcpChatClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcp_chat_client);
initView();
mTcpChatServer = new TcpChatServer(handler);
mTcpChatServer.start();
mTcpChatClient = new TcpChatClient(handler);
mTcpChatClient.start();
}
private void initView() {
mTextView = (TextView) findViewById(R.id.tcp_chat_text);
mEditText = (EditText) findViewById(R.id.tcp_chat_edit);
mClient = (Button) findViewById(R.id.tcp_chat_button);
mServer = (Button) findViewById(R.id.tcp_chat_button1);
mStringBuffer = new StringBuffer();
mClient.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String trim = mEditText.getText().toString().trim();
Runnable runnable = new Runnable() {
@Override
public void run() {
mTcpChatClient.sendMessage(trim);
}
};
mEditText.setText("");
Thread thread = new Thread(runnable);
if (thread.isAlive()) {
thread.run();
} else {
thread.start();
}
hideSoftInput();
}
});
mServer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String trim = mEditText.getText().toString().trim();
Runnable runnable = new Runnable() {
@Override
public void run() {
mTcpChatServer.sendMessage(trim);
}
};
mEditText.setText("");
Thread thread = new Thread(runnable);
if (thread.isAlive()) {
thread.run();
} else {
thread.start();
}
hideSoftInput();
}
});
}
public void hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
}
}
实现之后的大概样子是这样的
这个是在本机上测试的,当然也可以在不同手机上测试,只要确定俩个手机ip网段相同,可以互相访问就可以了
代码已上传GitHub https://github.com/renxh4/RXHApp 欢迎star