TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流(当应用层向TCP层发送用于网间传输的、用8位字节表示的数据流)的传输层通信协议。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
TCP传输涉及的对象与UDP不同,其需要通过三次握手确定连接的建立,建立连接之后进行数据的传输
连接建立——三次握手
TCP三次握手的过程如下:
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
连接断开——四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次握手,由于其是全双工,具体过程如下:
TCP不同于UDP,是面向连接的,因此必须先开服务端
Socket和ServerSocket
Socket:此类实现客户端套接字,套接字是两台机器间通信的端点。
构造函数:
ServerSocket:此类实现服务端套接字。服务器套接字等待请求通过网络传入,它基于该请求执行某些操作,然后可能想请求者返回结果。(一个服务端可以同时给多个客户端提供服务)
TCP传输,客户端建立的过程
1.创建tcp客户端Socket服务,使用的是Socket对象(建议该对象创建时明确目的地,即要连接的主机)
2.如果连接建立成功,说明数据传输通道已经建立(底层帮助建立),这个通道实际就是一个流(输入流,输出流),Socket流(网络IO流)
Socket流是底层建立好的,既然是流,说明这里是既有输入,又有输出,想要输入或者输出流对象,可以找Socket来获取,通过
如下两个方法获得字节流
InputStream getInputStream():返回此套接字(Socket)的输入流
OutputStream getOutputStream():返回此套接字的输出流
3.使用输出流,将数据写出
4.关闭资源
public class TCPClientDemo {
public static void main(String[] args)throws UnknownHostException,IOException {
//创建客户端Socket服务(UDP中称为发送端)
Socket socket = new Socket("127.0.0.1",10002);
//获取Socket流中的输出流
OutputStream out = socket.getOutputStream();
//使用输出流将指定的数据写出去
out.write("TCP演示例子".getBytes());//因为是字节流
//关闭资源
socket.close();//输出流out不用关闭,因为其是socket中的流,关闭了socket即可,关闭资源实际就是将(客户端与服务端)连接断开
}
}
建立tcp服务端的思路
1.创建服务端Socket服务,通过ServerSocket对象
2.服务端必须对外提供一个端口(如果不知道端口,即不知道让哪个程序进行解析),否则客户端无法连接
3.获取连接过来的客户端对象
4.通过客户端对象获取socket流,读取客户端发来的数据
5.关闭资源,关闭客户端,关闭服务端
public class TCPServerDemo {
public static void main(String[] args) throws IOException{
//创建服务端对象
ServerSocket ss = new ServerSocket(10002);
//获取连接过来的客户端对象
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
//通过Socket对象获取输入流,要读取客户端发来的数据
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(ip+":"+text);
s.close();
ss.close();//注意,服务端通常是不关闭的,因为在不断的提供服务
}
}
其中Socket s = ss.accept()是阻塞式,如果没有客户端对象,会一直等待
上述的Demo例子实现了客户端向服务端发送数据,服务端进行接收,但是实际上是双向的,本质都是利用Socket对象,利用OutputStream进行数据输出,利用InputStream进行数据读取:
也就是说在服务端通过accept()方法获取客户端Socket对象后,可以使用OutputStream对客户端进行回写:
客户端:
public class TCPClientDemo {
public static void main(String[] args)throws UnknownHostException,IOException {
Socket socket = new Socket("127.0.0.1",10002);
OutputStream out = socket.getOutputStream();
out.write("TCP演示例子".getBytes());//因为是字节流
//读取服务端返回的数据,使用Socket读取流
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
socket.close();
}
}
服务端:
public class TCPServerDemo {
public static void main(String[] args) throws IOException{
ServerSocket ss = new ServerSocket(10002);
Socket s = ss.accept();//阻塞式,如果没有客户端对象,会一直等待
String ip = s.getInetAddress().getHostAddress();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(ip+":"+text);
//使用客户端Socket对象的输出流给客户端返回数据
OutputStream out = s.getOutputStream();
out.write("收到".getBytes());
s.close();
ss.close();
}
}
运行结果:
需求:客户端输入字母数据,发送给服务端,服务端收到后显示在控制台,并将该数据转成大写返回给客户端,知道客户端输入“over”转换结束,即创建一个英文大写转换服务器
分析:上述提及客户端与服务端,因此使用TCP传输
客户端
public class TransClient {
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket s = new Socket("127.0.0.1",10004);
//获取键盘录入
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//将录入的信息发送给Socket输出流
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream));
//PrintWriter是一个非常实用的输出流,PrintWriter(OutputStream out, boolean autoFlush) 通过现有的 OutputStream 创建新的 PrintWriter
PrintWriter out = new PrintWriter(s.getOutputStream(),true);//打印流中true表示自动刷新
String line = null;
//Socket输入流,读取服务端返回的大写数据
//InputStreamReader可以把InputStream转换为Reader,即是一个适配器类(adapter)
BufferedReader burIn = new BufferedReader(new InputStreamReader(s.getInputStream()));//为了操作字符方便,使用BuffererReader
while ((line=bufr.readLine())!=null){
if ("over".equals(line))
break;
out.println(line);
//读取服务端发回的大写数据
String upperStr = burIn.readLine();
System.out.println(upperStr);
}
s.close();
}
}
服务端
public class TransServer {
/*
1.ServerSocket服务
2.获取Socket对象
3.源:Socket,读取客户端发过来的需要转换的数据
4.显示在控制台上
5.将数据转为大写并返回
*/
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10004);
Socket s = ss.accept();
//获取ip
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"......connected");
//获取Socket读取流并装饰
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
//获取Socket的输出流并装饰
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while ((line=bufIn.readLine())!=null){
System.out.println(line);
out.println(line.toUpperCase());
}
s.close();
ss.close();
}
}