TCP网络编程初探

TCP协议也是应用最广泛的协议,许多高级协议都是建立在TCP协议之上的,例如HTTP、SMTP等。

IP协议是一个分组交换,它不保证可靠传输。而TCP协议是传输控制协议,它是面向连接的协议,支持可靠传输和双向通信。TCP协议是建立在IP协议之上的,简单地说,IP协议只负责发数据包,不保证顺序和正确性,而TCP协议负责控制数据包传输,它在传输数据之前需要先建立连接,建立连接后才能传输数据,传输完后还需要断开连接。TCP协议之所以能保证数据的可靠传输,是通过接收确认、超时重传这些机制实现的。并且,TCP协议允许双向通信,即通信双方可以同时发送和接收数据。

JAVA网络编程——TCP协议

Socket套接字

Socket 的原意是“插座”,在计算机通信领域,Socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 Socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

当操作系统接收到一个数据包的时候,如果只有IP地址,它没法判断应该发给哪个应用程序,所以,操作系统抽象出Socket接口,每个应用程序需要各自对应到不同的Socket,数据包才能根据Socket正确地发到对应的应用程序。使用Socket套接字可以在IP地址的不同的端口下进行通信。

一个Socket就是由IP地址和端口号(范围是0~65535)组成,可以把Socket简单理解为IP地址加端口号。端口号总是由操作系统分配,它是一个0~65535之间的数字,其中,小于1024的端口属于特权端口,需要管理员权限,大于1024的端口可以由任意用户的应用程序打开。

使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。

因此,当Socket连接成功地在服务器端和客户端之间建立后:

  • 对服务器端来说,它的Socket是指定的IP地址和指定的端口号;
  • 对客户端来说,它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。

C/S通信模式,TCP网络编程初探

Server端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package Com.Dcm.Chat.Server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8866); // 监听指定端口
System.out.println("server is running...");
while(true) {
Socket sock = ss.accept();
System.out.println("connected from " + sock.getRemoteSocketAddress());
Thread t = new Handler(sock);
t.start();
}
}
}

class Handler extends Thread {
Socket sock;

public Handler(Socket sock) {
this.sock = sock;
}

@Override
public void run() {
try (InputStream input = this.sock.getInputStream()) {
try (OutputStream output = this.sock.getOutputStream()) {
System.out.println("Client " + sock.getRemoteSocketAddress() + " has connect to Server "
+ sock.getLocalSocketAddress() + " Success!");
handle(input, output);
}
} catch (Exception e) {
try {
this.sock.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
System.out.println("client disconnected.");
}
}

private void handle(InputStream input, OutputStream output) throws IOException {
var writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
writer.write("hello\n");
writer.flush();
while(true) {
String s = reader.readLine();
if (s.equals("bye")) { //读取bye后断开连接
writer.write("bye\n");
writer.flush();
break;
}
Instant ins = Instant.now();
ZonedDateTime zdt = ins.atZone(ZoneId.systemDefault());
System.out.println(zdt + " >>> " + s);
writer.write("ok: " + s + "\n");
writer.flush();
}
}
}

Client端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package Com.Dcm.Chat.Client;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class ClientDemo {
public static void main(String[] args) throws IOException {
//centOS主机 : 121.43.173.31; 本机IP:localhost或127.0.0.1;
Socket sock = new Socket("121.43.173.31", 8866); // 连接指定服务器和端口
try (InputStream input = sock.getInputStream()) {
try (OutputStream output = sock.getOutputStream()) {
handle(input, output);
}
}
sock.close();
System.out.println("disconnected.");
}

private static void handle(InputStream input, OutputStream output) throws IOException {
var writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
Scanner scanner = new Scanner(System.in);
System.out.println("[server] " + reader.readLine());
while(true) {
System.out.print(">>> "); // 打印提示
String s = scanner.nextLine(); // 读取一行输入
writer.write(s);
writer.newLine();
writer.flush();
String resp = reader.readLine();
System.out.println("<<< " + resp);
if (resp.equals("bye")) { //读取bye后,客户端关闭连接
break;
}
}
}
}

部署到服务器进行测试:

在部署到本地(localhost)测试成功后,我尝试在阿里云服务器上运行Server测试。

首先,使用xftp建立连接传输项目到服务器:

使用xftp建立连接传输项目到服务器

之后使用Xshell连接到服务器对项目进行操作:

输入相关代码启动服务:

注意:需要在阿里云控制台启用相关的端口,本次Server端使用8866端口,需要提前开启!

1
2
3
[root@CentOS ~]# cd /usr/local/src/ChatTogether
[root@CentOS ChatTogether]# cd out/production/ChatTogether/
[root@CentOS ChatTogether]# java Com.Dcm.Chat.Server.ServerDemo

使用IDEA开启Client端,向Server端传输数据:

使用IDEA开启Client端,向Server端传输数据

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
D:\SourceFiles\Java\jdk-11.0.5\bin\java.exe "-javaagent:D:\SourceFiles\JetBrains\IntelliJ IDEA 2021.1.1\lib\idea_rt.jar=61812:D:\SourceFiles\JetBrains\IntelliJ IDEA 2021.1.1\bin" -Dfile.encoding=UTF-8 -classpath E:\ProgramDemo\ChatTogether\out\production\ChatTogether Com.Dcm.Chat.Client.ClientDemo
[server] hello
>>> 你好,hello world!
<<< ok: 你好,hello world!
>>> 不要回答!
<<< ok: 不要回答!
>>> 消灭人类暴政,世界属于三体!
<<< ok: 消灭人类暴政,世界属于三体!
>>> 我们是同志了!
<<< ok: 我们是同志了!
>>> 北海,要多想!
<<< ok: 北海,要多想!
>>> 没关系的都一样。
<<< ok: 没关系的都一样。
>>>

服务端:

1
2
3
4
5
6
7
8
9
10
[root@CentOS ChatTogether]# java Com.Dcm.Chat.Server.ServerDemo
server is running...
connected from /119.102.250.226:61814
Client /119.102.250.226:61814 has connect to Server /172.30.89.4:8866 Success!
2021-08-08T20:44:06.982581+08:00[Asia/Shanghai] >>> 你好,hello world!
2021-08-08T20:44:14.471235+08:00[Asia/Shanghai] >>> 不要回答!
2021-08-08T20:44:26.662957+08:00[Asia/Shanghai] >>> 消灭人类暴政,世界属于三体!
2021-08-08T20:44:33.162085+08:00[Asia/Shanghai] >>> 我们是同志了!
2021-08-08T20:44:47.791487+08:00[Asia/Shanghai] >>> 北海,要多想!
2021-08-08T20:44:59.515964+08:00[Asia/Shanghai] >>> 没关系的都一样。

参考:廖雪峰Java教程 TCP编程 https://www.liaoxuefeng.com/wiki/1252599548343744/1305207629676577


-------------本文到此结束 感谢您的阅读-------------
谢谢你请我喝肥宅快乐水(๑>ڡ<) ☆☆☆