TCP 协议
- 一种面向连接的保证可靠传输的协议,通过TCP协议传输,得到一个顺序无差错的数据流。确保接收方完全正确的获取发送方所发送的全部数据
- TCP数据的传输之前必须连接,需要连接时间。TCp传输的效率不如UDP高(对数据的内容中正确性的检验必然需要占用计算的处理时间和网络宽带)
- 传输数据没有大小的限制,一旦连接建立起来,双方的Socket就可以按照统一的格式传输大的数据
- 可靠性:用于远程连接(TeLnet) 和 文件的传输(FTP)
- TCP的三次握手:传输前发送方和接收方成对的两个socket之间必须连接
- 第一次握手:客户主动(active open)去connect服务器,并且发送SYN 假设序列号为J,
服务器是被动打开(passive open)。并进入SUN_SEND状态,等待服务器确认 - 第二次握手:服务器在收到SYN后,它会发送一个SYN以及一个ACK(应答)给客户,
ACK的序列号是 J+1表示是给SYN J的应答,新发送的SYN K 序列号是K。此时服务器 进入SYN_RECV状态 - 第三次握手:客户在收到新SYN K, ACK J+1 后,也回应ACK K+1 以表示收到了。客户端和服务器端进入ESTABLISHED状态
- 三次握手完成。然后两边才可以开始数据发送了
基于TCP协议的Socket通信
- Socket 原理:
Socket:“插座”、" 套接字"
Socket提供了进程通信的端点,描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的 主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。客户软件使用不同编号的插座(Socket),就可以得到不同的服务(进程)。 - 连接过程
(1)服务器监听:是服务器端套接字并不指定具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和 端口号,然后就向服务器端套接字提出连接请求。
(3)连接确认:是指当服务器端套接字监听到或者说接收accept()到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把 服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端 套接字继续处于 监听状态,继续接收其他客户端套接字的连接请求。
————————————————
版权声明:本文为CSDN博主「songfelicity」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/songxinfeng1989/article/details/81027156
3. socket编程
a. 客户端与服务器均建立socket端口对象
socket = new Socket("localhost", 8010);//客户端需要一个 socket端口(包含服务器的地址和端口)
serverSocket = new ServerSocket(8010);//建立服务端的socket端(包含服务器的端口号)
b. 服务器保持接收状态
socket = serverSocket.accept();//服务端的socket端 接收 客户端的socket对象
c. 客户端通过输出流将内容(用户类对象)写入硬盘中
os = socket.getOutputStream(); //获得客户端的输出流(作用:将内容 序列化后 写入硬盘)
User user = new User("admin","123456");
ObjectOutputStream oos = new ObjectOutputStream(os); //对象输出流:面向 所有类型的对象
oos.writeObject(user);//利用输出流 将User类的对象 写入硬盘
socket.shutdownOutput();//关闭输出流
客户端将某个类对象要写入:
ObjectOutputStream(OutputStream X) 使得输出流转换为对象输出流ObjectOutputStream的方法:writeObject(Object X)
*其中的用户类需要implements Serializable:标识该类的对象可以被序列化
客户端通过输入流反序列化,读取硬盘中的序列化内容:
获得客户端的输出流》BufferedReader(Reader X)的readLine()以字符形式读取(Reader读的是字符 》InputStreamReader(InputStream):实现从字节流到字符流的转换)
is = socket.getInputStream();//获得客户端的输出流(作用:将内容 序列化为字节流后 写入硬盘)
br = new BufferedReader(new InputStreamReader(is));//BufferedReader(Reader X):该类readLine() 可以读取字符的流
//InputStreamReader(InputStream):实现从字节流到字符流的转换
String str = "";
while((str = br.readLine()) != null) { //以字符形式读取
System.out.println("接收到的服务器响应为:" + str);
}
d. 服务端利用循环和进程:使服务器一直处于接收状态
while(true){
try {
serverSocket = new ServerSocket(8010);//建立服务端的socket端(包含服务器的端口号)
socket = serverSocket.accept();//服务端的socket端 接收 客户端的socket对象
NewThread newThread = new NewThread(socket);
newThread.start();
} catch (IOException e) {
// e.printStackTrace(); //注释掉 》 不想让他打印出异常
}finally {
}
服务器接收客户端的内容:服务器接收 客户端的socket对象》socket.getInputStream()获得客户端的输入流》ObjectInputStream(InputStream X)获得所有类型的流》利用readObject()方法读取到用户类》输出该类的信息
服务器给客户端的应答:socket.getOutputStream()获得客户端的输出流》write方法写入服务器的应答信息
客户端的输入流:可以读取客户端的内容(输入流将硬盘上的序列化内容进行反序列化,完成读取)
客户端的输出流:可以将内容写入客户端(输出流将某类型的内容进行序列化,写入客户端的硬盘中)
public class NewThread extends Thread{
private Socket socket = null;
public NewThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream is = null;
ObjectInputStream ois = null;
OutputStream os = null;
try {
is = socket.getInputStream();//
ois = new ObjectInputStream(is);
User user = (User)ois.readObject();
System.out.println("我是服务器,我接收到的信息为:"+user.getName()+user.getPswd());
//服务器给客户端的应答
String str = "欢迎登陆";
os = socket.getOutputStream();
os.write(str.getBytes());//给流中写入字节
ois.close();
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
基于UDP协议的Socket通信
- 无连接协议,每个数据报都是一个独立的信息(包括完整的源地址或目的地址),因此无需建立发送方和接收方的连接
- 在网络以任何可能的路径传往目的地址,因此能否到达的地,到达目的地址的时间以及内容的正确性都是不能被保证,是一个不可靠的协议,发送方锁发送的数据并不一定以相同的次序发送到接收方
- 传输数据的大小上是有所限制的,每个被传输的数据必须限定在60KB之内。