目录
- 🦁TCP通信实现原理
- 🦁TCP单向通信
- 创建服务端
- 创建客户端
- 🦁TCP双向通信
- 创建服务端
- 创建客户端
- 🦁创建点对点的聊天应用
- 创建发送消息线程和接收消息线程
- 创建服务端
- 创建客户端
- 🦁优化点对点聊天应用
🦁TCP通信实现原理
前边我们提到TCP协议是面向的连接的,在通信时客户端与服务器端必须建立连接。在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。
请求响应模式:
- Socket类:发送TCP消息。
- ServerSocket类:创建服务器
**套接字Socket是一种进程间的数据交换机制。**套接字与主机地址和端口地址相关联。主机地址就是客户端或服务器程序所在的主机的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端口。(使用输入输出流进行通信)
TCP/IP套接字是最可靠的双向流协议,使用TCP/IP可以发送任意数量的数据
。
通过 Socket的编程顺序
创建服务器ServerSocket,在创建时,定义ServerSocket的监听端口(在这个端口接收客户端发来的消息)
ServerSocket调用accept()方法,使之处于阻塞状态。
创建客户端Socket,并设置服务器的IP及端口。
客户端发出连接请求,建立连接。
分别取得服务器和客户端Socket的InputStream和OutputStream。
利用Socket和ServerSocket进行数据传输。
关闭流及Socket。
🦁TCP单向通信
单向通信是指通信双方中,一方固定为发送端,一方则固定为接收端。
创建服务端
public class OneWaySocketServer {
public static void main(String[] args) {
System.out.println("服务端启动,开始监听。。。。。");
try(ServerSocket serverSocket = new ServerSocket(8888);
//监听8888端口,获与取客户端对应的Socket对象
Socket socket = serverSocket.accept();
//通过与客户端对应的Socket对象获取输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//通过与客户端对应的Socket对象获取输出流对象
PrintWriter pw = new PrintWriter(socket.getOutputStream())){
System.out.println("连接成功!");
while(true){
//读取客户端发送的消息
String str = br.readLine();
System.out.println("客户端说:"+str);
if("exit".equals(str)){
break;
}
pw.println(str);
pw.flush();
}
}catch(Exception e){
e.printStackTrace();
System.out.println("服务端启动失败。。。。。");
}
}
}
创建客户端
public class OneWaySocketClient {
public static void main(String[] args) {
//获取与服务端对应的Socket对象
try(Socket socket = new Socket("127.0.0.1",8888);
//创建键盘输入对象
Scanner scanner = new Scanner(System.in);
//通过与服务端对应的Socket对象获取输出流对象
PrintWriter pw = new PrintWriter(socket.getOutputStream());
//通过与服务端对应的Socket对象获取输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
while(true){
//通过键盘输入获取需要向服务端发送的消息
String str = scanner.nextLine();
//将消息发送到服务端
pw.println(str);
pw.flush();
if("exit".equals(str)){
break;
}
//读取服务端返回的消息
String serverInput = br.readLine();
System.out.println("服务端返回的:"+serverInput);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
tips:
都是先运行服务端再运行客户端哦!
🦁TCP双向通信
双向通信是指通信双方中,任何一方都可为发送端,任何一方都可为接收端。
创建服务端
public class TwoWaySocketServer {
public static void main(String[] args) {
System.out.println("服务端启动!监听端口8888。。。。");
try(ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
//创建键盘输入对象
Scanner scanner = new Scanner(System.in);
//通过与客户端对应的Socket对象获取输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//通过与客户单对应的Socket对象获取输出流对象
PrintWriter pw = new PrintWriter(socket.getOutputStream());){
while(true){
//读取客户端发送的消息
String str = br.readLine();
System.out.println("客户端说:"+str);
String keyInput = scanner.nextLine();
//发送到客户端
pw.println(keyInput);
pw.flush();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
创建客户端
public class TwoWaySocketClient {
public static void main(String[] args) {
try(Socket socket = new Socket("127.0.0.1", 8888);
//创建键盘输入对象
Scanner scanner = new Scanner(System.in);
//通过与服务端对应的Socket对象获取输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//通过与服务端对应的Socket对象获取输出流对象
PrintWriter pw = new PrintWriter(socket.getOutputStream());){
while (true) {
String keyInput = scanner.nextLine();
pw.println(keyInput);
pw.flush();
String input = br.readLine();
System.out.println("服务端说:" + input);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这种由于两个端都是在同一个线程上,所以不能随意发送信息,必须等待一个端接收到信息之后另一个端才能继续发送。
🦁创建点对点的聊天应用
可以像聊天那样随意发送消息,实际上就是另起发送消息和接收消息线程,这样发送和接收就可以同步进行。
创建发送消息线程和接收消息线程
/**
* 发送消息线程
*/
class Send extends Thread{
private Socket socket;
public Send(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.sendMsg();
}
/**
* 发送消息
*/
private void sendMsg(){
//创建Scanner对象
try(Scanner scanner = new Scanner(System.in);
//创建向对方输出消息的流对象
PrintWriter pw = new PrintWriter(this.socket.getOutputStream());){
while(true){
String msg = scanner.nextLine();
pw.println(msg);
pw.flush();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 接收消息的线程
*/
class Receive extends Thread{
private Socket socket;
public Receive(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.receiveMsg();
}
/**
* 用于接收对方消息的方法
*/
private void receiveMsg(){
//创建用于接收对方发送消息的流对象
try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));){
while(true){
String msg = br.readLine();
System.out.println("他说:"+msg);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
创建服务端
public class ChatSocketServer {
public static void main(String[] args) {
try(ServerSocket serverSocket = new ServerSocket(8888);){
System.out.println("服务端启动,等待连接。。。。。");
Socket socket = serverSocket.accept();
System.out.println("连接成功!");
new Send(socket).start();
new Receive(socket).start();
}catch(Exception e){
e.printStackTrace();
}
}
}
创建客户端
/**
* 用于发送消息的线程类
*/
class ClientSend extends Thread{
private Socket socket;
public ClientSend(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.sendMsg();
}
/**
* 发送消息
*/
private void sendMsg(){
//创建Scanner对象
try(Scanner scanner = new Scanner(System.in);
//创建向对方输出消息的流对象
PrintWriter pw = new PrintWriter(this.socket.getOutputStream());){
while(true){
String msg = scanner.nextLine();
pw.println(msg);
pw.flush();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 用于接收消息的线程类
*/
class ClientReceive extends Thread{
private Socket socket;
public ClientReceive(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.receiveMsg();
}
/**
* 用于接收对方消息的方法
*/
private void receiveMsg(){
//创建用于接收对方发送消息的流对象
try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));){
while(true){
String msg = br.readLine();
System.out.println("他说:"+msg);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
public class ChatSocketClient {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 8888);
System.out.println("连接成功!");
new ClientSend(socket).start();
new ClientReceive(socket).start();
}catch(Exception e){
e.printStackTrace();
}
}
}
🦁优化点对点聊天应用
前面的案例中,客户端和服务器都有大量重复代码(发送消息和接收消息),其实在网络编程中,编写客户端和服务器唯一的区别就是在实例化socket的时候不一样(一个是实例化ServerSocket,指定监听端口,通过ServerSocket获取客户端的socket,一个是实例化Socket),其它部分都是一致的,所以我们可以把接收信息线程和发送消息线程提取出来,客户端和服务端写在同一个实例里面,通过判断用户输入的信息来执行实例化服务器还是客户端。
/**
* 发送消息线程
*/
class Send extends Thread{
private Socket socket;
private Scanner scanner;
public Send(Socket socket,Scanner scanner){
this.socket = socket;
this.scanner = scanner;
}
@Override
public void run() {
this.sendMsg();
}
/**
* 发送消息
*/
private void sendMsg(){
//创建向对方输出消息的流对象
try(PrintWriter pw = new PrintWriter(this.socket.getOutputStream())){
while(true){
String msg = scanner.nextLine();
pw.println(msg);
pw.flush();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 接收消息的线程
*/
class Receive extends Thread{
private Socket socket;
public Receive(Socket socket){
this.socket = socket;
}
@Override
public void run() {
this.receiveMsg();
}
/**
* 用于接收对方消息的方法
*/
private void receiveMsg(){
//创建用于接收对方发送消息的流对象
try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()))){
while(true){
String msg = br.readLine();
System.out.println("他说:"+msg);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
public class GoodTCP {
public static void main(String[] args) {
Scanner scanner = null;
ServerSocket serverSocket = null;
Socket socket = null;
try{
scanner = new Scanner(System.in);
System.out.println("请输入:server,<port> 或者:<ip>,<port>");
String str = scanner.nextLine();
String[] arr = str.split(",");
if("server".equals(arr[0])){
//启动服务端
System.out.println("TCP Server Listen at "+arr[1]+" .....");
serverSocket = new ServerSocket(Integer.parseInt(arr[1]));
socket = serverSocket.accept();
System.out.println("连接成功!");
}else{
//启动客户端
socket = new Socket(arr[0],Integer.parseInt(arr[1]));
System.out.println("连接成功!");
}
//启动发送消息的线程
new Send(socket,scanner).start();
//启动接收消息的线程
new Receive(socket).start();
}catch(Exception e){
e.printStackTrace();
}finally{
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}