TCP实现聊天室

我们在写一个聊天室的小项目时候,需要有客户端和服务器端;并且他们必须分开开发,不能有任何互相调用的语句出现!(并且运行的时候先运行服务端(只能一个),然后可以运行多个客户端)

 

一般我们先写好服务端:

package 聊天室服务端;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class MyServer {
	public static List<Cthread> list=new ArrayList<Cthread>();
	public static void main(String[] args) {
		try {
			ServerSocket serversocket=new ServerSocket(9090);//创建一个服务端,端口号待会客户端连接要用
			System.out.println("服务端已经打开!等待连接....");
			while(true){//这里是一个无限循环,不停的等待着客户端来连接
			Socket socket=serversocket.accept();//这是一个堵塞方法,没人连接就不会往下面运行
			Cthread ct = new Cthread(socket);//每连上一个客户端,我就给他创建一个线程,专门去接收它的信息,以及广播给其他人
			ct.start();
			list.add(ct);//把线程都添加到一个列表,方便服务端管理
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

服务端还需要接收消息,还有广播消息的功能,所以没当连上一个客户端的时候,我们就给新建一个线程来专门接收它的消息,以及广播给其他人(客户端不与其他客户端有联系,所有信息发送给服务端,然后由它广播)

package 聊天室服务端;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketAddress;


/**
 * ,专门去接收它的信息,以及广播给其他人

 * @author yan
 *
 */
public class Cthread extends Thread{
	Socket socket;
	private BufferedWriter bout;
	private SocketAddress addr;
	public 	Cthread(Socket socket){
		this.socket=socket;
	}
	public void run(){
		// 获得客户端地址
		addr = socket.getRemoteSocketAddress();
		System.out.println("连了一个客户端:" + addr);

		// 通过Socket对象获得IO流
		try {
			OutputStream ops = socket.getOutputStream();
			InputStream ips = socket.getInputStream();

			// 包装成缓冲字符流
			OutputStreamWriter osr = new OutputStreamWriter(ops);
			bout = new BufferedWriter(osr);

			InputStreamReader isr = new InputStreamReader(ips);
			BufferedReader br = new BufferedReader(isr);
			
			String msg = "欢迎光临!\r\n";
			bout.write(msg);
			bout.flush();
			while (true) {
				// 服务端读取客户端发来的消息
				// 读取一行数据,读取到换行回车就不读取了
				String str = br.readLine();
				System.out.println( str);

				// 收到一个消息,就将消息广播给其它socket
				for (int i = 0; i < MyServer.list.size(); i++) {
					Cthread ct = MyServer.list.get(i);
					ct.sendMsg(str + "\r\n");
				}
			}
		} catch (Exception e) {
			System.out.println("客户端已经断开连接!");
		}
	
	}
	// 通过socket给客户端发送消息
		public void sendMsg(String msg) throws IOException {
			this.bout.write(msg);
			this.bout.flush();
		}
}

 

服务端以及完成

----------------------------------------------------------------------------------------------

接下来便是客户端了:

Client类里面主要包含界面构建,事件的触发

连接服务端的语句:

    socket = new Socket("127.0.0.1", 9090);//利用IP地址和端口号,来连接正在运行的服务器

 

 

package 聊天室客户端;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;

public class Client extends JFrame implements ActionListener{
	private Socket socket;
	private BufferedWriter bout;
	private BufferedReader br;
	private JTextPane pane;
	private JTextPane area;
	private static JTextField jt;
	private JButton btn1;
	private static JTextPane pane3;//用来显示在线人数,所以让其他对象都公用(目前没有做完这个功能)
	private boolean line=true;

	public static void main(String[] args) {
		Client c = new Client();
			JOptionPane.showMessageDialog(null, "请先设置昵称!");
			//如果不设置昵称,那么对话时就是显示的IP地址
			
	}
	
	public Client() {
		this.setTitle("聊天室");
		this.setBounds(200, 100, 800, 500);
		this.setDefaultCloseOperation(3);
		this.setLayout(null);

		// 显示接受的消息内容
		pane = new JTextPane();
		pane.setEditable(false);
		JScrollPane jsp = new JScrollPane(pane);
		jsp.setBounds(10, 10, 550, 300);
		jsp.setAutoscrolls(true);
		this.add(jsp);

		// 要发送的内容
		area = new JTextPane();
		JScrollPane jsp2 = new JScrollPane(area);
		jsp2.setBounds(10, 320, 440, 130);
		this.add(jsp2);

		
		JLabel label = new JLabel();
		label.setText("请设置昵称:");
		label.setBounds(460, 310, 100, 30);
		this.add(label);

		jt = new JTextField();
		jt.setText("");
		jt.setBounds(460, 340, 100, 30);
		this.add(jt);

		btn1 = new JButton("连接");
		btn1.setBounds(460, 380, 100, 30);
		btn1.addActionListener(this);
		this.add(btn1);
		
		
		JButton btn2 = new JButton("发送");
		btn2.setBounds(460, 420, 100, 30);
		btn2.addActionListener(this);
		this.add(btn2);
		
		
		// 显示在线人(还没有完成)
		JLabel label3 = new JLabel();
		label3.setText("在线情况:");
		label3.setBounds(580, 0, 100, 30);
		this.add(label3);

		
		pane3 = new JTextPane();
		pane3.setEditable(false);
		JScrollPane jsp3 = new JScrollPane(pane3);
		jsp3.setBounds(580, 30,200, 420);
		jsp3.setAutoscrolls(true);
		this.add(jsp3);
		
		this.setResizable(false);
		this.setVisible(true);

	}
	
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
		if (command.equals("连接")) {
			try {
				// 和服务器建立连接
				socket = new Socket("127.0.0.1", 9090);//利用IP地址和端口号,来连接正在运行的服务器
				OutputStream ops = socket.getOutputStream();//通过socket对象获得输出流
				// 包装成缓冲字符流
				OutputStreamWriter osr = new OutputStreamWriter(ops);
				bout = new BufferedWriter(osr);
				InputStream ips = socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(ips);
				 br = new BufferedReader(isr);
				
				 // 创建一个接受消息的线程,用来接受服务端发来的消息(把需要用到的参数传过去)
				ReceiveThread rt = new ReceiveThread(br, pane,jt,pane3);
				rt.start();
				
				JOptionPane.showMessageDialog(null, "连接成功");
				btn1.setText("已连接");
				btn1.setEnabled(false);//设置点击连接后,连接按钮显示为不可以点击
			//	bout.write(jt.getText()+".929144493");//把名字发出去

			} catch (Exception ef) {
				JOptionPane.showMessageDialog(null, "连接失败");
			}
		} else if (command.equals("发送")) {
			// 获得输入框中输入的消息内容
			String msg = area.getText();
			if (msg == null || msg.equals("")) {
				JOptionPane.showMessageDialog(null, "必须输入消息内容!");
			} else {
				try {
					if(!jt.getText().equals("")){
						//获取名字和需要发送的信息,一起发送出去
						bout.write(jt.getText()+" say:"+msg + "\r\n");
					}else{
						//没有名字的话,就获取IP地址和需要发送的信息,一起发送出去
						bout.write(socket.getLocalAddress()+" 说:"+msg + "\r\n");
					}
					bout.flush();
					area.setText("");//清空发送框
				} catch (Exception ef) {
					ef.printStackTrace();
				}

			}

		}
	
	}

}

 

当我们连接上服务端的时候,我们就新建一个线程类来接收服务端发来的消息(也就是服务端广播的消息),并且我们利用这个线程类来改变我们聊天界面的文字显示;

 

package 聊天室客户端;


import java.io.BufferedReader;

import javax.swing.JTextField;
import javax.swing.JTextPane;

/**
 * 客户端接收消息的线程
 * 
 * @author kowloon
 * 
 */
public class ReceiveThread extends Thread {

	private BufferedReader br;
	private JTextPane pane;// 显示消息的文本框
	private JTextField jt;
	private JTextPane pane3;
 	public ReceiveThread(BufferedReader br, JTextPane pane,JTextField jt,JTextPane pane3) {
		this.br = br;
		this.pane = pane;
		this.jt=jt;
		this.pane3=pane3;
	}

	@Override
	public void run() {
		try {
			while (true) {//只要一创建,就一直在运行
				// 读取到一个字符串
				String msg = br.readLine() +"\r\n";//读取服务器广播过来的信息
//				int i=msg.indexOf(".");
//				String panduan=msg.substring(i,i+10);
//				if(panduan.equals(".929144493")){
//					//传名字
//					String name=msg.substring(0,i);
//					pane3.setText(pane3.getText()+name+"\r\n");
//					System.out.println("名字收到!");
//				}else{//信息
					String text = pane.getText();//获得我自己的聊天框里面的信息
					pane.setText(text + msg);//把信息添加到聊天框里面,不需要换行符,因为读取数据的时候已经有了换行符
				//	System.out.println("信息收到");
			//	}
			}

		} catch (Exception ef) {
			ef.printStackTrace();
		}
	}

}

 

 

 

 

 

 

 

 

 

 下面还有一些运行时候的图片,还有所有完整的项目压缩包!

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最多能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器的IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。
<计算机网络实验> 基于TCP的网络聊天室的设计 -实验指导 一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最多能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器的IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值