socket相关

Posted by Chenyawei on 2020-04-15
Words 2.3k and Reading Time 9 Minutes
Viewed Times

编写一个网络应用程序,有客户端和服务器端,客户端向服务器发送一个字符串,服务器收到该字符串后将其打印到命令行上,然后向客户端返回该字符串的长度,最后,客户端输出输出服务器端返回的该字符串的长度,分别用TCP和UDP两种方式去实现?

image-20200415111442901

tcp:

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package com.java.interview.socket;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
* @Author: chenyawei
* @Date: 2020-04-14 18:10
* @Description:
*/
public class TCPServer
{
public static void main(String[] args) throws IOException {
//创建socket,并将socket绑定到65000端口
ServerSocket serverSocket = new ServerSocket(65000);
//死循环,使得socket一直等待并处理客户端发送过来的请求
while(true){
//监听65000端口,直到客户端返回连接信息后才返回
Socket socket = serverSocket.accept();
//获取客户端端请求信息后,执行相关业务逻辑
new LengthCalculator(socket).start();
}
}
}


package com.java.interview.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
* @Author: chenyawei
* @Date: 2020-04-14 18:09
* @Description:
* SOCK_STREAM:表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,
* 如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,
* 因为要确保数据的正确性,否则网页不能正常解析。

*/
public class TCPClient {
public static void main(String[] args) throws IOException {
//创建socket,并指定连接的是本机的端口号为65000的服务器socket
Socket socket = new Socket("127.0.0.1",65000);
//获取输出流
OutputStream os = socket.getOutputStream();
//获取输入流
InputStream is = socket.getInputStream();
//将要传递给server的字符串参数转换成byte数组,并将数组写入到输出流中
os.write(new String("hello world").getBytes());
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入的内容,存成byte数组,ch主要用来读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换字符串,这里是从服务端回发回来的字符串参数的长度
String content = new String(buff,0,ch);
System.out.println(content);
//不要忘记关闭输入输出流以及socket
is.close();
os.close();
socket.close();
}
}


package com.java.interview.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* @Author: chenyawei
* @Date: 2020-04-14 18:16
* @Description:
*/
public class LengthCalculator extends Thread {
//以socket为成员变量
private Socket socket;

public LengthCalculator(Socket socket){
this.socket = socket;
}

@Override
public void run(){
try {
//获取socket端输出流
OutputStream os = socket.getOutputStream();
//获取socket端输入流
InputStream is = socket.getInputStream();
int ch = 0;
byte[] buff = new byte[1024];
//buff主要用来读取输入端内容,存成byte数组,ch主要用来获取读取数组的长度
ch = is.read(buff);
//将接收流的byte数组转换成字符串,这里获取的主要内容是客户端发送过来的字符串
String content = new String(buff,0,ch);
System.out.println(content);
//往输出流里写入获得的字符串的长度,回发给客户端
os.write(String.valueOf(content.length()).getBytes());
//关闭流及socket
is.close();
os.close();
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}

udp:

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
69
70
71
72
package com.java.interview.socket;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @Author: chenyawei
* @Date: 2020-04-14 18:10
* @Description:
*/
public class UDPServer {
public static void main(String[] args) throws IOException {
//服务器端接受客户端发送的数据报
DatagramSocket socket = new DatagramSocket(65001);//监听的端口号
byte[] buff = new byte[100];//存储从客户端接受到的内容
DatagramPacket packet = new DatagramPacket(buff,buff.length);
//接受客户端发送过来的内容,并将内容封装进DatagramPacket对象中
socket.receive(packet);

byte[] data = packet.getData();//从DatagramPacket对象中获取到真正存储的数据
//将数据从二进制转换成字符串形式
String content = new String(data,0,packet.getLength());
System.out.println(content);
//将要发送给客户端的数据转换成二进制
byte[] sendedContent = String.valueOf(content.length()).getBytes();
//服务端给客户端发送数据报
//从DatagramPacket对象中获取到数据端来源地址和端口号
DatagramPacket packetToClient = new DatagramPacket(sendedContent,sendedContent.length,
packet.getAddress(),packet.getPort());
socket.send(packetToClient);
}
}


package com.java.interview.socket;
import java.io.IOException;
import java.net.*;
/**
* @Author: chenyawei
* @Date: 2020-04-14 18:10
* @Description:
* SOCK_DGRAM:表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,
* 如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,
* 无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
*
*/
public class UDPClient {
public static void main(String[] args) throws IOException {
//客户端发送数据给服务端
DatagramSocket socket = new DatagramSocket();
//要发送给服务端端的数据
byte[] buf = "hello world".getBytes();
//将IP地址封装成InetAddress对象
InetAddress address = InetAddress.getByName("127.0.0.1");
//将要发送给服务端的数据封装成DatagramPacket对象,需要填写ip地址与端口号
DatagramPacket packet = new DatagramPacket(buf,buf.length,address,65001);
//发送数据给服务端
socket.send(packet);

//客户端接受服务端发送过来的数据报
byte[] data = new byte[100];

//创建DatagramPacket对象用来存储服务端发送过来的数据
DatagramPacket receivedPacket = new DatagramPacket(data,data.length);
//将接受到的数据存储到DatagramPacket对象中
socket.receive(receivedPacket);
//将服务端发送过来的数据取出来并打印到控制台
String content = new String(receivedPacket.getData(),0,receivedPacket.getLength());
System.out.println(content);
}
}

Socket原理

1、什么是Socket

在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据
 socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。
 我的理解就是Socket就是该模式的一个实现:即socket是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。
 Socket()函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

2、网络中进程如何通信

既然Socket主要是用来解决网络通信的,那么我们就来理解网络中进程是如何通信的。

2.1、本地进程间通信

a、消息传递(管道、消息队列、FIFO)
 b、同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)?【不是很明白】
 c、共享内存(匿名的和具名的,eg:channel)
 d、远程过程调用(RPC)

2.2、网络中进程如何通信

我们要理解网络中进程如何通信,得解决两个问题:
 a、我们要如何标识一台主机,即怎样确定我们将要通信的进程是在那一台主机上运行。
 b、我们要如何标识唯一进程,本地通过pid标识,网络中应该怎样标识?
解决办法:
 a、TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机
 b、传输层的“协议+端口”可以唯一标识主机中的应用程序(进程),因此,我们利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互

3、Socket怎么通信

现在,我们知道了网络中进程间如何通信,即利用三元组【ip地址,协议,端口】可以进行网络间通信了,那我们应该怎么实现了,因此,我们socket应运而生,它就是利用三元组解决网络通信的一个中间件工具,就目前而言,几乎所有的应用程序都是采用socket,如UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰)。
Socket通信的数据传输方式,常用的有两种:
 a、SOCK_STREAM:表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。
 b、SOCK_DGRAM:表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
 例如:QQ 视频聊天和语音聊天就使用 SOCK_DGRAM 传输数据,因为首先要保证通信的效率,尽量减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质的影响


notice

欢迎访问 chenyawei 的博客, 若有问题或者有好的建议欢迎留言,笔者看到之后会及时回复。 评论点赞需要github账号登录,如果没有账号的话请点击 github 注册, 谢谢 !

If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !