TCP编程
一、TCP概念
TCP,Transmission Control Protocol,传输控制协议,基于字节流的传输层通信协议。
特点:
- 安全、可靠
- 面向连接
- 传输数据大小限制,一旦连接建立,双方可以按统一的格式传输大的数据
- 效率低
TCP的三次握手
- 客户端向服务端发送一个请求
- 服务端收到请求后,回客户端一个响应
- 客户端向收到服务端的响应后,回服务端一个确认信息
二、基于TCP的Socket(套接字)通信模型
TCP 网络程序是指利用 Socket 编写的通信程序。利用 TCP 协议进行通信的两个应用程序是有主次之分的,一个是服务器程序,一个是客户端程序,两者的功能和编写方法不太一样。其中 ServerSocket 类表示 Socket 服务器端,Socket 类表示 Socket 客户端,两者之间的交互过程如下:
- 服务器端创建一个 ServerSocket(服务器端套接字),调用 accept() 方法等待客户端来连接。
- 客户端程序创建一个 Socket,请求与服务器建立连接。
- 服务器接收客户的连接请求,同时创建一个新的 Socket 与客户建立连接,服务器继续等待新的请求。
Socket通信模型:
三、ServerSocket和Socket
1、ServerSocket 类
ServerSocket 类是与 Socket 类相对应的用于表示通信双方中的服务器端,用于在服务器上开一个端口,被动地等待数据(使用 accept() 方法)并建立连接进行数据交互。
服务器套接字一次可以与一个套接字连接,如果多台客户端同时提出连接请求,服务器套接字会将请求连接的客户端存入队列中,然后从中取出一个套接字与服务器新建的套接字连接起来。若请求连接大于最大容纳数,则多出的连接请求被拒绝;默认的队列大小是 50。
(1)ServerSocket 的构造方法和常用方法
构造方法:
构造方法 | 描述 |
ServerSocket() | 无参构造方法。 |
ServerSocket(int port) | 创建绑定到特定端口的服务器套接字。 |
ServerSocket(int port,int backlog) | 使用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口。 |
ServerSocket(int port,int backlog,InetAddress bindAddr) | 使用指定的端口、监听 backlog 和要绑定到本地的 IP 地址创建服务器。 |
在上述方法的参数中 port 指的是本地 TCP 端口,backlog 指的是监听 backlog,bindAddr 指的是要将服务器绑定到的 InetAddress。
常用方法:
方法 | 描述 |
Socket accept() | 监听并接收到此套接字的连接。 |
void bind(SocketAddress endpoint) | 将 ServerSocket 绑定到指定地址(IP 地址和端口号)。 |
void close() | 关闭此套接字。 |
InetAddress getInetAddress() | 返回此服务器套接字的本地地址。 |
int getLocalPort() | 返回此套接字监听的端口。 |
SocketAddress getLocalSoclcetAddress() | 返回此套接字绑定的端口的地址,如果尚未绑定则返回 null。 |
int getReceiveBufferSize() | 获取此 ServerSocket 的 SO_RCVBUF 选项的值,该值是从 ServerSocket 接收的套接字的建议缓冲区大小。 |
调用 accept() 方法会返回一个和客户端 Socket 对象相连接的 Socket 对象。服务器端的 Socket 对象使用 getOutputStream() 方法获得的输出流,将指定客户端 Socket 对象使用 getInputStream() 方法获得那个输入流。同样,服务器端的 Socket 对象使用的 getInputStream() 方法获得的输入流,将指向客户端 Socket 对象使用的 getOutputStream() 方法获得的那个输出流。也就是说,当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之同样如此。
2、Socket
Socket 类表示通信双方中的客户端,用于呼叫远端机器上的一个端口,主动向服务器端发送数据(当连接建立后也能接收数据)。
(1)Socket 类的构造方法和常用方法
构造方法
方法 | 描述 |
Socket() | 无参构造方法。 |
Socket(InetAddress address,int port) | 创建一个流套接字并将其连接到指定 IP 地址的指定端口。 |
Socket(InetAddress address,int port,InetAddress localAddr,int localPort) | 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
Socket(String host,int port) | 创建一个流套接字并将其连接到指定主机上的指定端口。 |
Socket(String host,int port,InetAddress localAddr,int localPort) | 创建一个套接字并将其连接到指定远程地址上的指定远程端口。Socket 会通过调用 bind() 函数来绑定提供的本地地址及端口。 |
在上述方法的参数中,address 指的是远程地址,port 指的是远程端口,localAddr 指的是要将套接字绑定到的本地地址,localPort 指的是要将套接字绑定到的本地端口。
常用方法:
方法 | 描述 |
void bind(SocketAddress bindpoint) | 将套接字绑定到本地地址。 |
void close() | 关闭此套接字。 |
void connect(SocketAddress endpoint) | 将此套接字连接到服务器。 |
InetAddress getInetAddress() | 返回套接字的连接地址。 |
InetAddress getLocalAddress() | 获取套接字绑定的本地地址。 |
InputStream getInputStream() | 返回此套接字的输入流。 |
OutputStream getOutputStream() | 返回此套接字的输出流。 |
SocketAddress getLocalSocketAddress() | 返回此套接字绑定的端点地址,如果尚未绑定则返回 null。 |
SocketAddress getRemoteSocketAddress() | 返回此套接字的连接的端点地址,如果尚未连接则返回 null。 |
int getLoacalPort() | 返回此套接字绑定的本地端口。 |
intgetPort() | 返回此套接字连接的远程端口。 |
四、基于TCP的Socket编程
示例一:
客户端发送数据,服务器接收消息,然后两个Socket相继关闭。
服务器:
package basis.stuJavaNet.StuTCP_1;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args){
ServerSocket server = null;
BufferedReader br = null;
try {
server = new ServerSocket(10086);
System.out.println("服务器已启动。。。");
//监听端口
Socket socket = server.accept();
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String data = br.readLine();
System.out.println("客户端说:"+data);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
br.close();
server.close();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("服务器已关闭。");
}
}
客户端:
package basis.stuJavaNet.StuTCP_1;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args){
Socket client = null;
BufferedWriter bw = null;
try {
client = new Socket("localhost",10086);
System.out.println("客户端已启动。。。");
bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
String data = "Hello,张三";
bw.write(data);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bw.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("客户端关闭。");
}
}
示例二:
客户端发送数据,服务器接收并回复,客户端接收服务器的回复,然后客户端服务器相继关闭。
服务器:
package basis.stuJavaNet.StuTCP_2;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args){
ServerSocket server = null;
try {
server = new ServerSocket(10086);
System.out.println("服务器已启动。。。");
//监听端口
Socket socket = server.accept();
InputStream inputStream = socket.getInputStream();
byte[] arr = new byte[1024];
int len = inputStream.read(arr);
String data = new String(arr,0,len);
System.out.println("...");
String address = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
System.out.println("客户端"+address+":"+port+"说:"+data);
//服务器回复
OutputStream outputStream = socket.getOutputStream();
outputStream.write("李四,你也好,好久不见。".getBytes());
outputStream.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
server.close();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("服务器已关闭。");
}
}
客户端:
package basis.stuJavaNet.StuTCP_2;
import java.io.*;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args){
Socket client = null;
try {
client = new Socket("localhost",10086);
System.out.println("客户端已启动。。。");
OutputStream outputStream = client.getOutputStream();
String data = "张三,你好,好久不见。";
outputStream.write(data.getBytes());
outputStream.flush();
System.out.println("...");
InputStream inputStream = client.getInputStream();
byte[] arr = new byte[1024];
int len = inputStream.read(arr);
String reply = new String(arr,0,len);
System.out.println("...");
System.out.println("服务器回复:"+reply);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("客户端关闭。");
}
}
示例三:
客户端上传文件,服务器接收文件,以图片为例。
服务器:
package basis.stuJavaNet.StuTCP_3;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args){
ServerSocket server = null;
try {
server = new ServerSocket(10086);
System.out.println("服务器已启动。。。");
Socket socket = server.accept();
System.out.println("客户端已连接。。。");
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("e:test//haha.jpg")));
byte[] arr = new byte[1024];
int len = 0;
while ((len=bis.read(arr))!=-1){
bos.write(arr,0,len);
bos.flush();
}
System.out.println("上传文件成功。。。");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("服务器已关闭。");
}
}
客户端:
package basis.stuJavaNet.StuTCP_3;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args){
Socket client = null;
try {
client = new Socket("localhost",10086);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("e:111.jpg")));
BufferedOutputStream bos = new BufferedOutputStream(client.getOutputStream());
byte[] arr = new byte[1024];
int len = 0;
while ((len = bis.read(arr))!=-1){
bos.write(arr,0,len);
bos.flush();
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
client.close();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("客户端已关闭。");
}
}
示例四:
多个客户端和一个服务器端通信(多线程)
服务器:
package basis.stuJavaNet.StuTCP_4;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TcpServer {
public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket(10086);
System.out.println("服务器已启动。。。");
while (true){
Socket client = server.accept();
ServerThread thread = new ServerThread(client);
thread.start();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
class ServerThread extends Thread{
private Socket client;
public ServerThread(){}
public ServerThread(Socket client){
this.client = client;
}
@Override
public void run() {
try {
OutputStream outputStream = client.getOutputStream();
InputStream inputStream = client.getInputStream();
Scanner scanner = new Scanner(System.in);
while (true){
byte[] arr = new byte[1024];
int len = inputStream.read(arr);
String message = new String(arr,0,len);
//约定
if(message.equals("886")||message.equals("baibai")){
String clientName = client.getInetAddress().getHostAddress();
System.out.println(clientName+"已退出连接");
break;
}
System.out.println("来自客户端的消息:"+message);
//回复消息
System.out.println("服务器对客户端说:");
String message1 = scanner.next();
outputStream.write(message1.getBytes());
outputStream.flush();
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端:
package basis.stuJavaNet.StuTCP_4;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class TCPClient {
public static void main(String[] args) {
Socket client = null;
try {
client = new Socket("localhost",10086);
OutputStream outputStream = client.getOutputStream();
InputStream inputStream = client.getInputStream();
Scanner input = new Scanner(System.in);
while (true){
System.out.println("客户端对服务器说:");
String message = input.nextLine();
//发送
outputStream.write(message.getBytes());
outputStream.flush();
//约定
if (message.equals("886")||message.equals("baibai")){
break;
}
//接受消息
byte[] arr = new byte[1024];
int len = inputStream.read(arr);
String reply = new String(arr,0,len);
System.out.println("服务器回复:"+reply);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
示例五:
TCP实现注册登录。
服务器:
package basis.stuJavaNet.StuTCP_5;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
public class TCPServer {
private ServerSocket registerSocket;//用来注册
private ServerSocket loginSocket;//用来登录
private Properties userList = new Properties();//存储用户
{
try {
userList.load(new BufferedInputStream(new FileInputStream(new File("userList.properties"))));
} catch (IOException e) {
e.printStackTrace();
}
}
public void start(){
//开启注册服务
new Thread(){
@Override
public void run() {
startRegisterService();
}
}.start();
//开启登录服务
new Thread(){
@Override
public void run() {
startLoginService();
}
}.start();
}
private void startRegisterService(){
try {
this.registerSocket = new ServerSocket(6666);
while (true){
Socket socket = registerSocket.accept();
System.out.println(socket+"连接到服务器。");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String message = reader.readLine();
System.out.println(message);
String[] arr = message.split(",");
if (this.userList.containsKey(arr[0]) && this.userList.containsKey(arr[1])){
System.out.println("用户名已存在。");
}else {
this.userList.setProperty(arr[0],arr[1]);
this.userList.store(new BufferedOutputStream(new FileOutputStream(new File("userList.properties"))),"add an user");
System.out.println("注册成功。");
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
this.registerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void startLoginService(){
try {
this.loginSocket = new ServerSocket(7777);
Socket socket = this.loginSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String message = reader.readLine();
String[] arr = message.split(",");
if(this.userList.containsKey(arr[0])){
if (arr[1].equals(this.userList.getProperty(arr[0]))){
System.out.println("登录成功。");
}else {
System.out.println("用户名或密码错误。");
}
}else {
System.out.println("还未注册,请先注册");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
package basis.stuJavaNet.StuTCP_5;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class TCPClient {
public void doRegister(String userName,String password){
realAction(userName,password,"localhost",6666);
}
public void doLogin(String userName,String password){
realAction(userName,password,"localhost",7777);
}
private void realAction(String userName,String password,String ip,int port){
Socket client = null;
try {
client = new Socket(ip,port);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
writer.write(userName+","+password);
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试:
第一步:开启服务器
package basis.stuJavaNet.StuTCP_5;
public class StartServer {
public static void main(String[] args) {
TCPServer server = new TCPServer();
server.start();
}
}
第二步:开启一个注册客户端
package basis.stuJavaNet.StuTCP_5;
public class TestRegister {
public static void main(String[] args) {
TCPClient client = new TCPClient();
client.doRegister("lisi","123");
}
}
第三步:开启一个登录客户端
package basis.stuJavaNet.StuTCP_5;
public class TestLogin {
public static void main(String[] args) {
TCPClient client = new TCPClient();
client.doLogin("lisi","123");
}
}