TCP编程中加入多线程

本文介绍了在TCP编程中如何利用多线程处理多个客户端连接,以及实现发送-接收交替模式的线程间通信。通过创建独立线程处理Socket对象的输入流和输出流,服务器和客户端使用wait()和notify()进行同步,确保数据的正确传递。示例代码展示了如何在Java中实现这一机制。
摘要由CSDN通过智能技术生成

TCP编程中加入多线程

今天学习的时候了解到,TCP编程中,服务器会可能有多个外来客户端的连接,产生的多个Socket对象。

对于这种情况,我们希望每当有一个Socket对象产生,服务器程序就产生一个线程去处理这个Socket对象

那么服务器端口的main线程可以当作一个获取Socket对象的线程,阻塞式产生Socket对象,一旦获取就创建线程去处理

ServerSocket ss = new ServerSocket(55565);
for(;;) {
	Socket s = ss.accept();
	Thread t = new MyThread(s);
	t.start();
}

class MyThread extends Thread {
	@Override
	public void run() {
		// TODO...
	}
}

另外,我们还可以让客户端或者服务器端实现发送数据->接受数据->发送数据的交替模式。

这里使用到了线程间通信模式,每一个Socket对象都创建两个线程分别处理其返回的InputStreamOutputStream,两个线程间通过wait()notify()通信。

比如,客户端

Socket s = new Socket("127.0.0.1", 55565);
new Thread(new CustomerHandleOutputStream(s)).start();
new Thread(new CustomerHandleInputStream(s)).start();

class CustomerHandleOutputStream implements Runnable {
	// TODO
}
class CustomerHandleInputStreamimplements Runnable {
	// TODO
}

完整代码如下

服务器端

main方法入口
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    /**
     * boolean getMessage = 判断是否获取了客户端发来的信息
     * 默认为false
     */
    private static boolean isGetMessage = false;

    /**
     * @param isGetMessage=设置getMessage
     */
    public static void setGetMessage(boolean isGetMessage) {
        Server.isGetMessage = isGetMessage;
    }
    
    public static boolean getIsGetMessage() {
        return isGetMessage;
    }
    
    /**
     * 启动服务器
     */
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(55565);
        System.out.println("建立服务器完毕");
        while(true) {
            Socket s = ss.accept();
            new Thread(new ServerHandleInputStream(s)).start();
            new Thread(new ServerHandleOutputStream(s)).start();
        }
    }
}
处理服务器端的InputStream
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 服务器端用来处理输入流的线程任务
 */
public class ServerHandleInputStream implements Runnable {
    private Socket socket;
    private final BufferedReader reader;

    /**
     *
     * @param socket=服务器端Socket获取的输入流
     */
    public ServerHandleInputStream(Socket socket) throws IOException {
        this.socket = socket;
        this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    }


    /**
     * 当服务器没有收到消息的时候,线程进入等待
     *          收到消息的时候,线程获取每行内容并打印
     */
    @Override
    public void run() {
        for (int i = 0; i < 5; ++i) {
            synchronized (socket) {
                while(!Server.getIsGetMessage()) {
                    try {
                        socket.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                try {
                    String text = reader.readLine();
                    System.out.println("收到来自客户端" + text);
                    Server.setGetMessage(false);    // 读取后,将getMessage设置为false
                    socket.notifyAll();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
处理服务器端的OutputStream
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;

/**
 * 服务器处理输出流的类
 */
public class ServerHandleOutputStream implements Runnable {
    private final Socket socket;
    private final PrintStream ps;
    /**
     *
     * @param socket=服务器端Socket获取的输出流
     */
    public ServerHandleOutputStream(Socket socket) throws IOException {
        this.socket = socket;
        this.ps = new PrintStream(socket.getOutputStream());
    }
    /**
     * 当服务器收到消息的时候,线程进入等待
     *          没有收到消息的时候,线程开始发送消息
     */
    @Override
    public void run() {
        for (int i = 0; i < 5; ++i) {
            synchronized (socket) {
                while(Server.getIsGetMessage()) {
                    try {
                        socket.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("向客户端发送信息");
                ps.println("你好,客户端!");
                Server.setGetMessage(true);     // 发送后将getMessage设置为true
                socket.notifyAll();
            }
        }
    }
}

客户端

main方法入口
import java.io.*;
import java.net.Socket;

public class Customer {
    private static boolean isGetMessage = false;

    public static void setIsGetMessage(boolean isGetMessage) {
        Customer.isGetMessage = isGetMessage;
    }
    public static boolean getIsGetMessage() {
        return isGetMessage;
    }
    public static void main(String[] args) throws IOException, InterruptedException {
        Socket s = new Socket("127.0.0.1", 55565);
        Thread t1 = new Thread(new CustomerHandleOutputStream(s));
        Thread t = new Thread(new CustomerHandleInputStream(s));
        t.start();
        t1.start();
        Thread.sleep(10000);
    }
}
客户端处理OutputStream
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class CustomerHandleOutputStream implements Runnable {
    private final Socket socket;
    private final PrintStream ps;
    
    public CustomerHandleOutputStream(Socket socket) throws IOException {
        this.socket = socket;
        ps = new PrintStream(socket.getOutputStream());
    }
    
    @Override
    public void run() {
        for (int i = 0; i < 5; ++i) {
            synchronized (socket) {
                while(Customer.getIsGetMessage()){
                    try {
                        socket.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("向服务器发送信息!");
                ps.println("你好,服务器!");
                Customer.setIsGetMessage(true);
                socket.notifyAll();
            }
        }
    }
}
处理InputStream
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

public class CustomerHandleInputStream implements Runnable{
    private final Socket socket;
    private final BufferedReader reader;

    public CustomerHandleInputStream(Socket socket) throws IOException {
        this.socket = socket;
        this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; ++i) {
            synchronized (socket){
                System.out.println("收到来自服务器:");
                while(!Customer.getIsGetMessage()) {
                    try {
                        socket.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                try {
                    String text = reader.readLine();
                    System.out.println("收到来自服务器:" + text);
                    Customer.setIsGetMessage(false);
                    socket.notifyAll();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果
ServerSocket
在这里插入图片描述

突然想到某个注意事项:

对于这里的类似single-ProducerThread和single-ConsumerThread,在判断Thread是否进入WAITING状态的语句可以使用if(flag),但是对于multi-ProducerThread和multi-ConsumerThread来说,必须使用while(flag),这是因为当一个producer thread调用notifyAll(),唤醒其他所有等待该锁的线程时,很有可能会被一个被唤醒的producer thread抢到锁的执行权,那么该抢到锁的producer thread会接着执行wait()后面的代码执行——继续生产,那么就会出现p->p->p->p->c->c的线程切换的情况,而不是p->c->c->c。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值