您的位置:首页 > 理论基础 > 计算机网络

网络编程详解----TCP/UDP

2020-07-14 06:19 387 查看

1、概述

网络编程从大的方面说就是对信息的发送到接收,中间传输为物理线路的作用。

网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的

1.1、计算机网络

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

1.2、网络编程的目的

传播交流信息、数据交换达到通讯的目的。

想要达到这个效果需要什么

(1)如何准确的定位网络上的一台主机 192.168.16.124 端口,定位到这个计算机上的某个资源。

(2)找到了这个主机,如何传输?

javaweb: 网页编程 B/S

网络编程: TCP/IP C/S

2、网络通信的要素

如何实现网络的通信?

2.1、通信双方的地址:

  • IP
  • 端口号
  • 192.168.16.124:8052

2.2、规则:网络通信的协议

hhtp, ftp,smtp,tcp,udp,…

2.3、TCP/UDP模型

2.4、小结

  • 网络编程中有两个主要的问题

    如何准确的定位到网络上的一台或者多台主机

    找到主机之后如何进行通信

  • 网络编程中的要素

    IP和端口号

    网络通信协议

3、IP

ip地址:InetAddress

用来唯一定位一台网络上计算机,例如:127.0.0.1:本机

3.1、ip地址的分类

(1)ipv4/ipv6

  • IPV4:127.0.0.1,由四个字节组成。0-255,总共42亿~
  • IPV6:128位。8个无符号整数,例如:2001:0221:aa12:0000:22ss:bb23:1314:1245

(2)公网(互联网)-私网(局域网)

  • ABCD类地址
  • 192.168.xx.xx专门给组织内部使用

3.2、域名

便于记忆,例如:www.baidu.com

InetAddress的常用方法的使用如下:

public class TestInetAddress {
public static void main(String[] args) {
try {
//查询本机地址
InetAddress byName = InetAddress.getByName("127.0.0.1");
System.out.println(byName);
InetAddress localhost = InetAddress.getByName("localhost");
System.out.println(localhost);
InetAddress localHost2 = InetAddress.getLocalHost();
System.out.println(localHost2);

//查询网站ip地址
InetAddress byName1 = InetAddress.getByName("www.baidu.com");
System.out.println(byName1);

//常用方法
System.out.println(byName1.getAddress());
System.out.println(byName1.getCanonicalHostName());//规范的名字
System.out.println(byName1.getHostAddress());//ip
System.out.println(byName1.getHostName());//域名,或者自己电脑的名字
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}

4、端口

端口表示计算机上一个程序的进程,不同的进程有着不同的端口号,用来区分不同的软件。端口被规定范围是0-65535,TCP和UDP协议各有65535个端口,单个协议下,端口号不能冲突。

4.1、端口的分类

  • 公有端口0-1023

    HTTP:80 / HTTPS:443 / FTP :21 / Telent :23

  • 程序注册端口:1024-49151,分配用户或者程序

    Tomcat:8080 / MySQL:3306 / Oracle:1521

  • 动态、私有 49152-635535

    netstat -ano #查看本机所有的端口
    netstat -ano|findstr"5040"  #查看指定的端口
    tasklist|findstr "5040" #查看指定端口的进程
public class TestSocketAddress {

public static void main(String[] args) {
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8080);
InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost", 8080);
System.out.println(inetSocketAddress);
System.out.println(inetSocketAddress2);

System.out.println(inetSocketAddress.getAddress());
System.out.println(inetSocketAddress.getHostName());//地址
System.out.println(inetSocketAddress.getPort());//端口
}
}

5、通信协议

协议:就是一种约定。网络通信协议注重在:速率、传输码率、代码结构、传输控制…

5.1、TCP/IP协议簇

  • TCP: 用户传输协议
  • UDP:用户数据协议
  • IP: 网络互连协议

5.2、TCP和UDP对比

  • TCP:类似于打电话

    连接,稳定

    三次挥手,四次挥手

    客户端、服务端

    传输完成、释放连接、效率低

  • UDP:类似于发短信

    不连接,不稳定

    客户端、服务器没有明确的界限

    不管有没有准备好都可以发送

5.3、三次握手和四次挥手

  • 三次 握手

第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手

  • 四次挥手

(1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
(2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
(3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
(4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
(5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
(6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

————————————————
版权声明:本段为CSDN博主「青柚_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38950316/article/details/81087809

6、TCP实现聊天

6.1、服务端代码

(1)获得一个地址

(2)等待客户连接

(3)读取客户消息

(4)通过管道流来得到客户消息

public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket accept = null;
InputStream inputStream= null;
ByteArrayOutputStream bos = null;
try {
//1、获得一个地址
serverSocket = new  ServerSocket(9999);
//2、等待客户连接过来
accept = serverSocket.accept();
//3、读取客户的消息
inputStream = accept.getInputStream();
//4、管道流
bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len=inputStream.read(buffer))!=-1) {
bos.write(buffer, 0, len);
}
System.out.println(bos);
} catch (IOException e) {
e.printStackTrace();
}

6.2、客户端代码

(1)要知道服务器的地址和端口号

(2)创建一个Socket连接

(3)发送消息IO流

public static void main(String[] args) {
Socket socket = null;
OutputStream outputStream= null;
try {
//1、要知道服务器的地址,端口号
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
int port = 9999;
//2、创建一个socket连接
socket = new Socket(serverIP,port);
//3、发送信息IO流
outputStream = socket.getOutputStream();
outputStream.write("你好!".getBytes());
} catch (Exception e) {
e.printStackTrace();
}

运行时先运行服务器端,再运行客户端,服务器端就能接受到客户端发送的消息。记住代码的最后都需要关闭资源。

7、TCP实现文件上传

7.1、客户端代码

(1)创建一个Socket连接

(2)创建一个输出流

(3)读取文件

(4)写出文件

(5)关闭资源(代码中未给出)

public static void main(String[] args) throws Exception {
//1、创建一个Socket连接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 8999);
//2、创建一个输出流
OutputStream outputStream = socket.getOutputStream();
//3、读取文件
FileInputStream fileInputStream = new FileInputStream(new File("20200616221039.png"));
//4、写出文件
byte[] buffer = new byte[1024];
int len;
while ((len=fileInputStream.read(buffer))!=-1) {
outputStream.write(buffer, 0, len);
}
System.out.println(outputStream.toString());
}

7.2、服务端代码

(1)创建服务

(2)监听客户端连接

(3)获取输入流

(4)文件输出

(5)关闭资源(代码中未给出)

public static void main(String[] args) throws Exception {
//1、创建服务
ServerSocket serverSocket = new ServerSocket(8999);
//2、监听客户的连接
Socket socket = serverSocket.accept();//阻塞式监听,直到监听到客户端的连接
//3、获取输入流
InputStream inputStream = socket.getInputStream();
//4、文件输出
FileOutputStream fileOutputStream = new FileOutputStream(new File("receive.png"));
byte[] buffer= new byte[1024];
int len;
while ((len=inputStream.read(buffer))!=-1) {
fileOutputStream.write(buffer, 0, len);
}
}

8、UDP实现聊天

8.1、UDP实现单向发送消息

(1)send方代码

​ a、建立一个Socket

​ b、建一个包

​ c、发送包

​ d、关闭流

public static void main(String[] args) throws Exception {
//1、建立一个Socket连接
DatagramSocket socket = new DatagramSocket(8080);
//2、建个包(用读取控制台的方式  System.in)
while(true) {//用while循环实现持续发送消息
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6667));
//3、发送包
socket.send(packet);
if (data.equals("bye")) {
break;//当控制台键入bye的时候跳出循环
}
}
//4、关闭连接
socket.close();
}

(2)receive方代码

​ a、开放端口

​ b、接受数据包

​ c、关闭连接

public static void main(String[] args) throws Exception {
//1、开放端口
DatagramSocket socket = new DatagramSocket(6667);
//2、接收数据包
while(true){//用while语句来保证持续接收数据包
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
socket.receive(packet);
System.out.println(new String(packet.getData(),0,packet.getLength()));
String str = new String(packet.getData(),0,packet.getLength());
if(str.equals("bye")) {//当对方输入bye的时候跳出循环
break;
}
}
socket.close();
}

8.2、用多线程实现双向咨询

(1)send方代码

public class TalkSend implements Runnable {

DatagramSocket socket = null;
BufferedReader reader = null;
private int fromport;
private int toport;
private String toip;

public TalkSend(int fromport,int toport, String toip) {
this.toport = toport;
this.toip = toip;
this.fromport= fromport;
try {
socket = new DatagramSocket(fromport);
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e) {
e.printStackTrace();
}
}

@Override
public void run() {
while(true) {

try {
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toip, this.toport));
socket.send(packet);
if (reader.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}

(2)receive方代码

public class TalkRecieve implements Runnable {
DatagramSocket socket=null;
private int fromport;
private String fromname;

public TalkRecieve(int fromport,String fromname) {
this.fromport = fromport;
this.fromname = fromname;
try {
socket = new DatagramSocket(fromport);
} catch (SocketException e) {
e.printStackTrace();
}
}

@Override
public void run() {
while(true){
try {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
socket.receive(packet);
String str = new String(packet.getData(),0,packet.getData().length);
System.out.println(fromname+":"+str);
if (str.equals("bye")){
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
socket.close();
}
}

(3)开启多线程

用户1

public class TalkStudent {
public static void main(String[] args) {
new Thread(new TalkSend(7777,9999,"localhost")).start();
new Thread(new TalkRecieve(8888,"teacher")).start();
}
}

用户2

public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkSend(5555,8888,"localhost")).start();
new Thread(new TalkRecieve(9999,"student")).start();
}
}

用户1和用户2都能够发送和接收消息。

9、URL

9.1、URL的组成

协议://ip地址:端口/项目名/资源(可以少,不能多)

9.2、URL的方法

代码:

public class UrlTest {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=zhangsan&password=123");
System.out.println(url.getProtocol());//协议
System.out.println(url.getHost());//主机ip
System.out.println(url.getPort());//端口
System.out.println(url.getPath());//文件
System.out.println(url.getFile());//全路径
System.out.println(url.getQuery());//参数
}
}

运行结果

http
localhost
8080
/helloworld/index.jsp
/helloworld/index.jsp?username=zhangsan&password=123
username=zhangsan&password=123
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: