网络编程_一个简单的聊天雏形


                        此聊天程序,可以作为java网络学习的入门例子,程序虽小,五脏俱全,不过很多细节问题需要花时间完善。

1、聊天系统主要有两个模块:Client和Server
2、Client端功能:
       ①产生一个聊天窗口
      ②根据服务端的ip+端口后,通过Socket连接到服务端
       ③向服务端发送消息,并接受服务端的消息
   ④同时将消息显示在聊天窗口上面
   
3、Server端功能:
 ①监听端口
  ②接受服务端的请求,并与服务端建立连接
 ③接受消息,转发消息至所有服务端


4、代码ChatClient.java

    

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

public class ChatClient extends Frame {

	private Socket s = null;
	DataOutputStream dos;
	DataInputStream dis;
	private boolean bConnected = false;

	TextField tfTxt;
	TextArea taContent;
	// 定义接收消息的线程
	Thread tRecv = new Thread(new RecvThread());

	public static void main(String[] args) {
		new ChatClient().lanuchFrame();
	}

	public void lanuchFrame() {
		this.setLocation(400, 300);
		this.setSize(300, 300);

		tfTxt = new TextField();
		taContent = new TextArea();

		taContent.setEditable(false);
		this.add(tfTxt, BorderLayout.SOUTH);
		this.add(taContent, BorderLayout.NORTH);
		this.pack();
		// 添加匿名监听器
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				disConnect();
				System.exit(0);
			}

		});
		tfTxt.addActionListener(new TFListener());
		this.setVisible(true);
		connect();
		tRecv.start();
	}

	/**
	 * 连接服务器
	 */
	public void connect() {
		try {   
			s = new Socket("127.0.0.1", 8888); //这个地方用来添加服务器ip+端口,根据实际改
			dos = new DataOutputStream(s.getOutputStream());
			dis = new DataInputStream(s.getInputStream());
			System.out.println("connected!");
			bConnected = true;
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void disConnect() {

		/*
		try {
			// ①让接收消息的线程停下来
			bConnected = false;
			// ②结束线程
            tRecv.join();
            
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				dos.close();
				dis.close();
				s.close();
			} catch (IOException e) {
				// TODO Auto-generated catch
				e.printStackTrace();
			}
		}
		*/

		try {
			// ①让接收消息的线程停下来
			bConnected = false;
			// ②结束线程
			tRecv.join();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 添加监听器的三种方式:①外部类 ②内部类 ③匿名类
	private class TFListener implements ActionListener {
		@Override
		public void actionPerformed(ActionEvent e) {
			String str = tfTxt.getText().trim();
			// taContent.setText(str);
			tfTxt.setText("");

			try {
				dos.writeUTF(str);
				dos.flush();
				/*
				 * //当dis关闭时,里面包装的流都会被关闭, 导致发一次消息就不能再放第二次了,所有要注释掉dos.close();
				 */
				// dos.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

		}

	}

	private class RecvThread implements Runnable {

		@Override
		public void run() {

			try {
				while (bConnected) {
					String str = dis.readUTF();
					// System.out.println(str);
					taContent.setText(taContent.getText() + str + "\n");
				}
			} catch (SocketException e) {
				System.out.println("退出了!和你们说88了");

			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("退出了!和你们说88了");
			}

		}

	}

}

5、代码ChatServer.java

     

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

/**
 * 设计思路:1 在主线程里面启动一个ServerSocket,不停的监听客户端的请求
 * 2:每当接收到了一个客服端,将其Socket包装到一个线程里面,让线程来 处理服务器与客户端的通信
 *  3:这样主线程用来接收客服端,子线程用来和每个客户端通信
 *
 * 注意:内部类的权限和非静态实例的一样,不能在静态方法里面new内部类, 只能用外部实例来new内部类
 * 
 * @author Administrator
 * 
 */
public class ChatServer {
	ServerSocket ss = null;
	boolean started = false;

	List<Client> clients = new ArrayList<Client>();

	public static void main(String[] args) {
		new ChatServer().start();
	}

	public void start() {
		// ①建立一个ServerSocket

		try {
			ss = new ServerSocket(8888);
			started = true;
		} catch (BindException e1) {
			// TODO Auto-generated catch block
			System.out.println("端口使用中...");
			System.out.println("请关闭相关的程序并重新运行服务器!");
			System.exit(0);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		// ②连接客户端
		try {
			while (started) {
				Socket s = ss.accept();
				// 服务器接收一个Socket后,启动一个线程来处理客服端的
				// 通信

				Client c = new Client(s);
				System.out.println("a client connected!");
				// 使用线程来接收客服端的信息
				new Thread(c).start();
				clients.add(c);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				ss.close();// 关闭ServerSocket
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	/*
	 * 客户端在服务端的一个包装
	 */
	class Client implements Runnable {

		private Socket s;
		private DataInputStream dis = null;
		private DataOutputStream dos = null;
		private boolean bConnected = false;

		public Client(Socket s) {
			this.s = s;
			try {
				dis = new DataInputStream(s.getInputStream());
				dos = new DataOutputStream(s.getOutputStream());
				bConnected = true;
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void send(String str) {
				try {
					dos.writeUTF(str);
				} catch (IOException e) {
					clients.remove(this);
					System.out.println("对方退出了!我从List里面去掉了");
//					e.printStackTrace();
				}
		}

		@Override
		public void run() {
			Client c=null;
			try {
				while (bConnected) {
					String str = dis.readUTF();
					// 每接收一个字符串,就全部转发给其他的客户端
					System.out.println(str);
//					for (Client c : clients) {
//						c.send(str);
//					}
					for(int i=0;i<clients.size();i++){
						c=clients.get(i);
						c.send(str);
					}

				}
			} 	catch (EOFException e) {
				System.out.println("Client closed!");
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					if (dis != null)
						dis.close();
					if (dos != null)
						dos.close();
					if (s != null) {
						s.close();
//						s=null;
					}
					
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				
			}

		}

	}
}

6、运行:1)运行ChatServer.java:①为其指定监听端口,我这里设置为8888,

                 2)运行ChatClient.java:①为其指定连接的ip+端口:这里我用的是本地127.0.0.1:8888,这根据ChatServer运行的主机为主

    3)效果图:

      



  

你可能感兴趣的:(socket,String,dos,服务器,聊天)