网络编程(TCP/IP篇)
1 为什么要进行网络编程
为了实现数据共享,这个世界有着无数的计算机,如果每台计算机都只是作为一个独立的个体来存在,那么它的功能将被大大限制,而引入计算机网络,使其能够与其它计算机进行数据交换,信息交流,比如我们知道的什么局域网啊,互联网啊这些,网络能够使得使用计算机的人们能够共享知识,共同解决问题,进而提高人类的生活水平,促进社会进步(你看计算机网络多重要)!
那么如何使得计算机在网络里面能够正常有效地发挥它信息共享的作用呢,就需要引入网络编程这门技术,来使得计算机能够在网络中更好地为人类服务。
2 IP和端口号
由于网络中的计算机有无数台,如果进行信息的交换,不可能说信息的发送与接收是混乱的,它必须得是有确定发送方与接收方,就像你要聊天,你得先确认倾听者是谁,他叫什么名字。那么计算机通信也是这样,通信中的两台计算机(我们称为客户端和服务器端)彼此必须明确对方是谁,当然,计算机不以名字作为标识,而是用它的IP地址来作为它的唯一定位。其次,人们在聊天的时候,总能区分出对方聊的是什么,家国情怀,仁人志士,那倾听者肯定会聊共同的话题,不可能你聊家国情怀,我突然冒出一句”早恋不好“。而对于计算机而言,它是通过端口号来区别双方的数据交换之间该用哪个程序来处理,QQ来的消息,我就QQ处理。微信来的消息,我就微信处理,不能用微信去处理QQ的消息。
2.1 IP地址
当计算机被接入网络中的时候,网络管理员会为这台计算机分配一个IP地址来作为这台计算机的唯一标识,值得一提的是,这个IP地址并不就是一定不变的,我们可以人为地设置IP地址,只要在网络内不要有冲突就行。
事实上,我们在访问某一个网页的时候,其实也是通过这个网页所在服务器的IP地址去寻找到它,以百度为例,访问百度的过程实际上就是通过百度服务器的IP地址去找到该服务器并访问的过程,在网上可以查到百度首页的IP地址为119.75.217.109,但是我们实际上并没有输入它的IP地址,我们输入的是:
上图红框里面的内容我们称之为域名,而前面的**https://**在此暂不赘述,那么我们为什么可以通过域名能访问到百度首页呢?事实上,域名和IP的关系有点类似于名字与身份证号的关系,我们叫一个人都是叫某个人的名字,而不是叫身份证号,可以理解为域名是IP的一种别称,它的存在满足了人类方便记忆的需求,当然,这个比喻有点牵强,人类的名字是可以重复的,而域名却是不能重复的。身份证号是唯一的,而计算机IP地址在某些情况下是可以修改的。
2.2 127.0.0.1、localhost、IP的关系
127.0.0.1:实际上也是IP地址,只不过是这种IP地址是本机对本机的,如果你不与外界进行沟通而仅进行本机的访问,比如说在电脑上运行你的客户端和服务器端,让他俩进行通信,那么你可以认为此时你的IP是127.0.0.1,但是,这种IP意义仅仅存在与你本机进行自我访问的时候。
Localhost:针对127.0.0.1的域名,同样的道理。这种域名的意义仅仅存在与你本机进行自我访问的时候
IP:此处的IP其实可以叫做本机IP,在网络中进行访问时,这个IP是计算机与外界通信的地址,它是对外开放的,就像2.1中提到的百度首页的IP地址一样。
2.3 java中有关IP的代码测试
在java中,提供了许多关于IP的封装好的方法,我们直接调用即可:
package com.JX;
import java.net.InetAddress;
import java.net.UnknownHostException;
//测试ip
public class TestInetAddress
{
public static void main(String[] args)
{
try
{
//查询本机地址
InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress1);
InetAddress inetAddress3 = InetAddress.getByName("localhost");
System.out.println(inetAddress3);
InetAddress inetAddress4 = InetAddress.getLocalHost();
System.out.println(inetAddress4);
//查询网站IP地址
InetAddress inetAddress2 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress2);
//常用方法
System.out.println(inetAddress2.getAddress());
System.out.println(inetAddress2.getCanonicalHostName());//规范的名字
System.out.println(inetAddress2.getHostAddress());//ip
System.out.println(inetAddress2.getHostName());//域名 自己电脑的名字
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
}
}
运行结果:
2.3 端口号
端口号存在的意义:用来区分计算机上的不同进程,可以理解为区分不同的软件,端口号被规定为0 ~ 65535,但是并不是在这个范围内我们都能任意取值,在编写软件时,我们仅能取1024 ~ 49151这个范围内的值,即使在这个范围内,也有许多常用的端口号已经被使用在了常用的软件上,比如:
- Tomcat:8080
- MySQL:3306
这些都是约定俗成的东西,我们不要去动它
3 TCP/IP通信协议
综合前面所说,我们已经知道了网络中你想要交流的计算机的定位信息(IP与端口),接下来要做的就是与它进行通信了,计算机之间的通信就跟人与人之间的交流一样,你们正常交流的基础是你们之间得有会话的方式来确保彼此能够理解,比如规定对话的语言是什么,语速不能太快或者太慢等等,这种规定在计算机网络中叫做通信协议,常见的有TCP/IP,UDP等,在本文我们只讨论TCP/IP协议。
3.1 TCP/IP
这种传输是一种稳定的传输,所谓稳定,就是保证双方保持连接的情况下,数据传送与接收不丢失,能够保证双方之间的通信正常进行。类似与人与人之间的打电话,首先会将电话接通,在确认双方通话线路畅通的情况下进行对话。
为了达到“保持双方稳定连接”的效果,TCP/IP中有“三次握手机制”,具体可以去百度一下它的详细内容,下面是百度百科对于“三次握手机制”的图示
接下来对上图模拟一个对话以便于理解"三次握手机制":
- 客户端:服务器,你在吗?(序号1)
- 服务器:哎,老弟,我在,咋滴啦?(序号2)
- 客户端:来来来,我跟你说个事儿。。。(序号3)
经过上述“模拟对话”,我们的客户端和服务器端便建立起了有效稳定的连接,接下来,就是进行数据交流啦!
3.2 TCP/IP编程案例
在java中,同样有大量的API支持TCP/IP编程,通常的思路是这样的:
客户端:
- 连接服务器Socket
- 发送消息
服务器:
- 建立服务端口ServerSocket
- 等待用户的连接 accept
- 接收用户的消息
下面附上两种通信功能的程序,分别进行消息的传输以及文件的传送
3.2.1 消息发送/接收
客户端代码:
package com.JX;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class TcpClientDemo01
{
public static void main(String[] args)
{
Socket socket = null;
OutputStream os = null;
try
{
//1.需要知道服务器的地址,端口号
InetAddress serverIP = InetAddress.getByName("127.0.0.1");//上述提到的127.0.0.1的作用
int port = 9999;
//2.创建一个Socket连接
socket = new Socket(serverIP, port);
//3.发送消息 IO流
os = socket.getOutputStream();
os.write("你好,欢迎进入socket的世界".getBytes());
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(os!=null)
{
try
{
os.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (socket!=null)
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
服务器端代码:
package com.JX;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServerDemo01
{
public static void main(String[] args)
{
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try
{
//1.服务器本身就得有一个地址,上述提到的127.0.0.1
serverSocket = new ServerSocket(9999);
while (true)
{
//2.等待客户端连接过来,监听
socket = serverSocket.accept();
//3.读取客户端的消息
is = socket.getInputStream();
//管道流
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1)
{
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
}
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
//关闭资源
if (baos != null)
{
try
{
baos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (serverSocket != null)
{
try
{
serverSocket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
3.2.2文件上传/接收
客户端:
package com.JX;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class TcpClientDemo02
{
public static void main(String[] args) throws IOException
{
//1.创建socke连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
//2.创建一个输出流
OutputStream os = socket.getOutputStream();
//3.读取文件
FileInputStream fis = new FileInputStream(new File("123.jpg"));
//4.写出文件
byte[] buffer = new byte[1024];
int len;
while((len=fis.read(buffer))!=-1)
{
os.write(buffer,0,len);
}
//通知服务器 我已经结束了
socket.shutdownOutput();//我已经传输完了
//确定服务器接收完毕才能够断开连接
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while((len2=inputStream.read(buffer2))!=-1)
{
baos.write(buffer2,0,len2);
}
System.out.println(baos.toString());
//5.关闭资源
os.close();
fis.close();
socket.close();
}
}
服务器端:
package com.JX;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServerDemo02
{
public static void main(String[] args) throws IOException
{
//1.建立socket
ServerSocket serverSocket = new ServerSocket(9000);
//2.监听客户端,堵塞的方式
Socket socket = serverSocket.accept();
//3.获取输入流
InputStream is = socket.getInputStream();
//4.文件输出,管道
FileOutputStream fos = new FileOutputStream("receive.jpg");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1)
{
fos.write(buffer, 0, len);
}
//通知客户端我接收完毕了
OutputStream os = socket.getOutputStream();
os.write("我已经接收完毕,你可以断开".getBytes());
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
4 结语
这是我第一次写博客,对其中的概念部分用了许多口语化的表述,所以难免有叙述不完善,不合理的地方,程序部分参考的是B站大神狂神说的视频点击此处跳转,对于博客中存在的问题,还请各位看官批评指正~