聊天室版本1
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server1 {
public static void main(String[] args) throws IOException {
//在本机80002端口上启动服务
//如果端口被占用,这里会出现异常
ServerSocket ss = new ServerSocket(8002);
System.out.println("服务已启动。。。");
//等待客户端发起连接请求并建立连接通道
Socket s=ss.accept();
System.out.println("客户端已连接。。。");
//从Socket连接通道,获取流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
/*
*通信协议
*通信流程(谁收谁发?)、数据格式(视频,音频,文本,字符,图片)
*
*1.接受4个字符love(按字符的单字节值发送)
*2.向对方发送3个字符you
*
*/
for(int i=0;i<4;i++)
{
//接收,收一个打印一个,字符的字节值改为char类型
char c = (char)in.read();
System.out.print(c);
}
//发送:向对方发送字符串。
//getBytes,字符串转成字节值:把Unicode转系统默认编码,如gbk英文变单字节
out.write("you".getBytes());
out.flush();
//断开连接
s.close();
//停止服务释放端口
ss.close();
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client1 {
public static void main(String[] args) throws UnknownHostException, IOException {
//127.0.0.1 本机回环ip
//localhost 本机回环ip
//如果不出异常就已经建立了连接通道
Socket s = new Socket("localhost",8002);
//从连接通道取出流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
/*
* 通信协议
* 1.发送4个字符love
* 2.接受3个字符you
*/
out.write("love".getBytes());
out.flush();
for(int i=0;i<3;i++)
{
char c=(char) in.read();
System.out.print(c);
}
s.close();
}
}
ss.accept()方法是阻塞的
程序执行到它就暂停,后面的代码不执行
直到客户端建立连接通道,后面的代码才
继续执行
in.read()方法是阻塞的
从对方接收数据,如果收不到指定的个数,
等待对方数据
阻塞操作都会让程序暂停,影响后面代码
执行。阻塞操作应该有单独的线程来并行
执行。
这种等待建立连接和通信两个操作如果要并行
执行就要多线程。
服务器端:
1.死循环执行accept方法
2.与一个客户端建立连接,要继续等待下一个客户端
通信线程:
1.等待客户端的数据。
2.一个通信线程针对一个客户端,执行通信操作(接收数据)。每连上一个客户端,针对客户端都要有一个通信线程。
回声例子
客户端向服务器端发送的数
服务器原封不动地再发回来
EchoServer
package liaotian;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer {
public void start(){
//新启线程,循环执行accpet()
new Thread(){
public void run() {
try {
ServerSocket ss = new ServerSocket(8002);
while(true)
{
Socket s = ss.accept();
//Socket连接通道交给线程来并行执行通信过程
TonXinThread t=new TonXinThread(s);
t.start();
}
} catch (Exception e) {
System.out.println("端口被占用或者服务已经停止");
}
};
}.start();
}
class TonXinThread extends Thread{
/*
*流插在网络上,基本字节流+文本编码编码转换流+读一行BufferedReader
*/
Socket s;
public TonXinThread(Socket s) {
this.s=s;
}
@Override
public void run() {
try {
/*
*通信协议
*流程
*1.接收2.发送
*数据格式
*1.GBK编码字符
*2.一行文本,末尾要有一个换行符
*PW-PrintWriter
*BR-ISR-网络数流
*PW-OSW-网络输出流
*PW.println()
*/
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
PrintWriter out=new PrintWriter(new OutputStreamWriter(s.getOutputStream(),"GBK"));
String line;
while((line=in.readLine())!=null){
out.println(line);
out.flush();
System.out.println(line);
}
} catch (Exception e) {
}
System.out.println("客户端已断开");
}
}
}
EchoClient
package liaotian;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class EchoClient {
Scanner sc=new Scanner(System.in);
public void start(){
try {
Socket s = new Socket("localhost",8000);
BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
PrintWriter out=new PrintWriter(new OutputStreamWriter(s.getOutputStream(),"GBK"));
while(true){
System.out.println("输入exit退出");
String str=sc.nextLine();
if("exit".equals(str))
{
s.close();
break;
}
//把str发送给服务器
out.println(str);
out.flush();
//接收回声数据
String echo=in.readLine();
System.out.println("回声:"+echo);
}
} catch (Exception e) {
}
System.out.println("已经与服务器端口连接");
}
public static void main(String[] args) {
EchoClient client=new EchoClient();
client.start();
}
}
聊天天室版本2
缺点:没法输入完整的话
一个客户端对应一个通信线程对象
一个客户端断开,对应的通信线程对象发现断开,把自己从集合中移除
每次调用sendAll()遍历集合,调用remove移除集合,如果程序sendAll正在遍历,那么就不能往集合添加新的客户端,也通信线程也不能移除自己。要避免多线程情况下,对同一个集合操作,数据访问冲突的问题。要同步加锁,让线程抢集合的锁,所有对集合的访问,添加删除都得加锁。
synchronized (list) {
list.add(t);
}
synchronized (list) {
sendAll(name + "进入了聊天室,在线人数:" + list.size());
}
while ((line = in.readLine()) != null) {
synchronized (list) {
sendAll(name + "说:" + line);
}
}
package liaotian2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ChatServer {
// 用来收集所有的线程通信对象
private List<TongXinThread> list = new ArrayList<TongXinThread>();
public static void main(String[] args) {
ChatServer server=new ChatServer();
server.start();
}
public void start() {
new Thread() {
ServerSocket ss ;
@Override
public void run() {
try {
ss = new ServerSocket(8000);
System.out.println("服务已经启动");
while (true) {
Socket s = ss.accept();
TongXinThread t = new TongXinThread(s);
synchronized (list) {
list.add(t);
}
t.start();
}
} catch (Exception e) {
System.out.println("端口被占用或服务已经停止");
}finally{
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
class TongXinThread extends Thread {
Socket s;
BufferedReader in;
PrintWriter out;
String name;
public TongXinThread(Socket s) {
this.s = s;
}
// 向当前客户端发送
public void send(String msg) {
out.println(msg);
out.flush();
}
// 向所有客户端发送
public void sendAll(String msg) {
// 遍历通信线程对象,调用send方法
for (TongXinThread t : list) {
t.send(msg);
}
}
@Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
out = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "GBK"));
// 接收客戶端昵称
name = in.readLine();
// 群发消息XXX进入聊天室
synchronized (list) {
sendAll(name + "进入了聊天室,在线人数:" + list.size());
}
// 循环接收聊天内容并群发
String line;
while ((line = in.readLine()) != null) {
synchronized (list) {
sendAll(name + "说:" + line);
}
}
} catch (Exception e) {
}
// 当前客户端连接断开!
// 当前通信对象,从集合中移除
synchronized (list) {
list.remove(this);
System.out.println(name + "离开了聊天室,在线人数:" + list.size());
}
}
}
}
package liaotian2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class ChatClient {
private Socket s;
private BufferedReader in;
private PrintWriter out;
Scanner sc;
public static void main(String[] args) {
ChatClient client=new ChatClient();
client.start();
}
public void start() {
try {
s=new Socket("176.114.17.243", 8000);
in = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
out = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "GBK"));
System.out.println("nick name:");
sc = new Scanner(System.in);
String name = sc.nextLine();
out.println(name);
out.flush();
//等待输入聊天内容,并发送
new Thread(){
public void run() {
input();
}
}.start();
//等待输入聊天室内容并显示
new Thread(){
public void run() {
receive();
}
}.start();
sc.close();
} catch (Exception e) {
System.out.println("连接失败");
}
}
protected void input() {
//循环
while(true)
{
System.out.print("输入聊天内容:");
String s=sc.nextLine();
out.println(s);
out.flush();
}
}
protected void receive() {
try {
String line;
while((line=in.readLine())!=null)
{
System.out.println("* "+line);
}
} catch (Exception e) {
}
System.out.println("连接已断开");
}
}
聊天室客户端,连接服务器,输入昵称后,把昵称发出去。开始通信,接收聊天室别人的聊天内容。输入聊天内容后回车,群发给所有人,自己也可以看到。
1从服务器不停地收内容显示2等待客户端输入内容。这两个阻塞操作放入线程,一个输入线程,一个接收线程(接收到数据不停地打印)执行。
//等待输入聊天内容,并发送
new Thread(){
public void run() {
input();
}
}.start();
//等待输入聊天室内容并显示
new Thread(){
public void run() {
receive();
}
}.start();
聊天天室版本3
按回车输入聊天内容,输入过程不受打扰(不显示聊天室信息),再按回车聊天内容一次性全输出出来了。
接收线程把聊天内容暂时放入集合中。(从服务器接收内容,存入集合)——生产者
接收线程往集合里加入数据,(向打印线程)发通知
protected void receive() {
try {
String line;
while((line=in.readLine())!=null)
{
//受到聊天内容存入结合
synchronized (list) {
list.add(line);
list.notifyAll();//通知等待的打印线程
}
}
} catch (Exception e) {
}
System.out.println("连接已断开");
}
打印线程从集合获取数据打印。(从集合取出内容进行打印,打印要从头部获取数据,先加入的聊天内容,先打印显示,最后加入的数据,最后取出【队列LinkedList】,没有数据输入不缓存,直接打印)——消费者
//缓存聊天数据的集合
private LinkedList<String>list=new LinkedList<String>();
打印线程
protected void print() {
//循环打印数据
while(true){
synchronized (list) {
while((inputFlag==true)||(list.size()==0)){
try {
list.wait();
} catch (InterruptedException e) {
}
}
System.out.println(list.removeFirst());
}
}
}
输入线程
输入线程输入要保证打印线程不打印。
输入线程输入完成,(向打印线程)发通知。
//输入标记,没有输入内容是false,开始输入内容是true
private boolean inputFlag=false;
protected void input() {
System.out.print("按回车开始输入:");
//循环
while(true)
{
sc.nextLine();//接收回车
inputFlag=true;//改标记
System.out.print("输入聊天内容:");
String s=sc.nextLine();
out.println(s);
out.flush();
inputFlag=false;//改标记
//通知打印线程
synchronized (list) {
//******************
list.notifyAll();
}
}
}
回车输入inputFlag=true; 时打印线程等待,输入线程输入完成输入标记改为inputFlag=false;,通知打印线程打印
package liaotian3;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Scanner;
public class ChatClient {
private Socket s;
private BufferedReader in;
private PrintWriter out;
Scanner sc;
//缓存聊天数据的集合
private LinkedList<String>list=new LinkedList<String>();
//输入标记,没有输入内容是false,开始输入内容是true
private boolean inputFlag=false;
public static void main(String[] args) {
ChatClient client=new ChatClient();
client.start();
}
public void start() {
try {
s=new Socket("localhost", 8000);
in = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
out = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "GBK"));
System.out.println("nick name:");
sc = new Scanner(System.in);
String name = sc.nextLine();
out.println(name);
out.flush();
//等待输入聊天内容,并发送
new Thread(){
public void run() {
input();
}
}.start();
//等待输入聊天室内容
new Thread(){
public void run() {
receive();
}
}.start();
//打印线程
new Thread(){
public void run() {
print();
}
}.start();
} catch (Exception e) {
System.out.println("连接失败");
}
}
protected void print() {
//循环打印数据
while(true){
synchronized (list) {
while((inputFlag==true)||(list.size()==0)){
try {
list.wait();
} catch (InterruptedException e) {
}
}
System.out.println(list.removeFirst());
}
}
}
protected void input() {
System.out.print("按回车开始输入:");
//循环
while(true)
{
sc.nextLine();//接收回车
inputFlag=true;//改标记
System.out.print("输入聊天内容:");
String s=sc.nextLine();
out.println(s);
out.flush();
inputFlag=false;//改标记
//通知打印线程
synchronized (list) {
//******************
list.notifyAll();
}
}
}
protected void receive() {
try {
String line;
while((line=in.readLine())!=null)
{
//受到聊天内容存入结合
synchronized (list) {
list.add(line);
list.notifyAll();//通知等待的打印线程
}
}
} catch (Exception e) {
}
System.out.println("连接已断开");
}
}
package liaotian3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ChatServer {
// 用来收集所有的线程通信对象
private List<TongXinThread> list = new ArrayList<TongXinThread>();
public static void main(String[] args) {
ChatServer server=new ChatServer();
server.start();
}
public void start() {
new Thread() {
ServerSocket ss ;
@Override
public void run() {
try {
ss = new ServerSocket(8000);
System.out.println("服务已经启动");
while (true) {
Socket s = ss.accept();
TongXinThread t = new TongXinThread(s);
synchronized (list) {
list.add(t);
}
t.start();
}
} catch (Exception e) {
System.out.println("端口被占用或服务已经停止");
}finally{
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
class TongXinThread extends Thread {
Socket s;
BufferedReader in;
PrintWriter out;
String name;
public TongXinThread(Socket s) {
this.s = s;
}
// 向当前客户端发送
public void send(String msg) {
out.println(msg);
out.flush();
}
// 向所有客户端发送
public void sendAll(String msg) {
// 遍历通信线程对象,调用send方法
for (TongXinThread t : list) {
t.send(msg);
}
}
@Override
public void run() {
try {
in = new BufferedReader(new InputStreamReader(s.getInputStream(), "GBK"));
out = new PrintWriter(new OutputStreamWriter(s.getOutputStream(), "GBK"));
// 接收客戶端昵称
name = in.readLine();
// 群发消息XXX进入聊天室
synchronized (list) {
sendAll(name + "进入了聊天室,在线人数:" + list.size());
}
// 循环接收聊天内容并群发
String line;
while ((line = in.readLine()) != null) {
synchronized (list) {
sendAll(name + "说:" + line);
}
}
} catch (Exception e) {
}
// 当前客户端连接断开!
// 当前通信对象,从集合中移除
synchronized (list) {
list.remove(this);
System.out.println(name + "离开了聊天室,在线人数:" + list.size());
}
}
}
}