`

Java Tcp Socket聊天系统模型(主要为了学习线程)

阅读更多
TcpSocekt通信模型




Tcp Socket是面向连接的,所以Server端的accept()方法,一直等着客户端的连接,如果连接成功,则两者进行通信,这种是同步的,因为accept()一直在那儿等着,时刻的等着,实际中的聊天系统是采用异步方式,当有请求的时候就调用accept()方法,没有请求的时候该做什么就做什么去,不需要在那儿等着,不浪费资源,一种异步的方式。这个例子只是为了学习线程而准备的。
端口有TCP端口和UDP端口两种,端口号都是从0到65535,TCP端口在3层,UDP不是四层就是7层 TCP和UDP的协议也不相同,TCP比UDP安全,更多TCP和UDP区别上google,baidu。

服务器端编码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * 服务器端编码
 * @author 欧阳平 2009-3-17 
 */
public class ChatServer {
	static int port = 5566;//端口号
	static Vector<Client> clients = new Vector<Client>(10);//存储连接客户信息
	static ServerSocket server = null; //建立服务器socket
	static Socket socket = null; //套接字连接
	/**
	 * Constructs
	 */
	public ChatServer() {
		try {
			System.out.println("Server start...");
			server  = new ServerSocket(port); //初始化服务器套接字
			while (true) {
				socket = server.accept(); //等待连接
				System.out.println(socket.getInetAddress()+"连接\n");//得到客户机地址
				Client client = new Client(socket); //实例化一个客户线程(其中线程Client中有Socket,这里的的Socket只是起个过度作用)
				//
				clients.add(client);//增加客户线程到向量中
				client.start();//启动线程
				notifyChatRoom(); //监视聊天室连接变化
			}
		} catch (Exception ex) {
			ex.printStackTrace();//输出出错信息
		}
	}
	
	public static void notifyChatRoom() { //监视客户端线程
		StringBuffer newUser = new StringBuffer("newUser");
		for (int i = 0; i < clients.size(); i++) {
			Client c = (Client)clients.elementAt(i);
			newUser.append(":"+c.name); //客户端姓名字符串
		}
		sendClients(newUser);//发送信息到客户端
	}
	
	public static void sendClients(StringBuffer message) {
		for (int i= 0 ; i < clients.size(); i++) {
			Client client = (Client)clients.elementAt(i);//分别得到每个客户端的连接
			client.send(message);//发送信息
		}
	}
	
	public void closeAll() { //关闭所有连接
		while (clients.size() > 0 ) { //遍历整个Vector
			Client client = (Client) clients.firstElement(); //得到一个客户端
			try {
				client.socket.close();
			} catch(IOException ex) {
				ex.printStackTrace(); // 输出错误信息
			}
			clients.removeElement(client); //移出客户端
		}
	}
	
	public static void disconnect(Client c) {// 断开客户端
		try {
			System.err.println(c.ip+"断开连接\n");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		clients.removeElement(c);
		c.socket = null;
	}
	
	
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		new ChatServer();

	}
	class Client extends Thread {
		Socket socket;//连接端口
		String name ;//用户姓名
		String ip; //客户端ip地址
		BufferedReader reader;//输入流
		PrintStream ps;//输出流
		public Client(Socket s) {
			socket = s;
			try {
				reader = new BufferedReader(new InputStreamReader(s.getInputStream()));//得到输入流
				ps = new PrintStream(s.getOutputStream());//得到输出流
				String info = reader.readLine();//读取接收到的信息
				StringTokenizer stinfo = new StringTokenizer(info,":"); //分解字符串
				String head = stinfo.nextToken(); //获取关键字
				System.out.println(stinfo.toString());
				System.out.println(head);
				if (stinfo.hasMoreTokens()){
					name = stinfo.nextToken() ;//获取用户名
				}
				if (stinfo.hasMoreTokens()) {
					ip = stinfo.nextToken(); //获取IP地址
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
			System.out.println(name);
			System.out.println(ip);
		}
		
		public void send (StringBuffer msg) {
			ps.println(msg); //输出信息
			ps.flush();
		}
		public void run() {
			while (true) {
				String line = null;
				try {
					
					line = reader.readLine();
					System.out.println("line:"+line);
					
				} catch (IOException ex) {
					ex.printStackTrace(); //输出错误信息
					ChatServer.disconnect(this);//断开连接
					ChatServer.notifyChatRoom();//更新信息
					return ;
				}
				if (line == null) { //客户离开
					ChatServer.disconnect(this);
					ChatServer.notifyChatRoom();
					return ;
				}
				StringTokenizer st = new StringTokenizer(line,":");//分解字符串
				String keyword = st.nextToken();
				if (keyword.equals("MSG")) { //发送来的聊天信息
					StringBuffer msg = new StringBuffer("MSG:");
					msg.append(name); //在信息上增加用户名
					msg.append(st.nextToken("\0\n"));
					ChatServer.sendClients(msg);//发送聊天语句到各个客户端
					System.out.println(msg);
				} else if (keyword.equals("quit")) { //退出命令
					ChatServer.disconnect(this); //断开连接
					ChatServer.notifyChatRoom(); //刷新信息
				}
			}
		}
	}
	
}




//客户端编码
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Label;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.StringTokenizer;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * 基于Socket网络聊天程序 客户端编码
 * @author 欧阳平 2009-3-17 
 */
public class ChatClient extends JFrame  implements ActionListener,Runnable{
	
	TextField tfName = new TextField(15);//姓名输入文本域
	Button btConnect = new Button("连接");//连接按钮
	Button btDisconnect = new Button("断开连接");//断开连接按钮
	TextArea tfChat = new TextArea(8,27);//显示聊天信息文本域
	Button btSend = new Button("发送");
	TextField tfMessage = new TextField(30);//聊天输入
	java.awt.List list1  = new java.awt.List(9);//显示在线用户信息 
	Socket socket = null;//连接端口
	PrintStream ps = null;//输出流
	Listen listen = null;
	//监听线程类
	class Listen extends Thread {
		BufferedReader reader;
		PrintStream ps;
		String cname;
		Socket socket;
		ChatClient chatClient;
		public Listen(ChatClient client,String name,Socket socket) {
			try {
				this.chatClient = client;
				 this.socket = socket;
				 this.cname = name;
				 reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				 ps = new PrintStream(socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		public void run() {
			while (true) {
				String line=null ;
				try {
				line = reader.readLine(); //读取数据流
				System.out.println("客户端:"+line);
				
			}catch (IOException ex) {
				ex.printStackTrace();
				ps.println("quit");; //断开连接
				return;
			}
			StringTokenizer stinfo = new StringTokenizer(line,":"); //分解字符串
			String keyword = stinfo.nextToken();
			if (keyword.equals("MSG")) {
				chatClient.tfChat.append(line+"\n");
			}
			else if (keyword.equals("newUser")){
				chatClient.list1.clear();
				chatClient.list1.add("users", 0);
				int i = 1;
				while (stinfo.hasMoreTokens()) {
					chatClient.list1.add(stinfo.nextToken(), i++);
				}
			}
	   	}
		
	  }
	}
	public void actionPerformed(ActionEvent e) {
		try{
			if(e.getSource()==btConnect) { //点击连接按钮
				if (socket == null) {
					socket = new Socket(InetAddress.getLocalHost(),5566);//实例化一个套接字
					ps = new PrintStream(socket.getOutputStream());//获取输出流,写入信息
					StringBuffer info = new StringBuffer("info:");
					String userinfo = tfName.getText()+":"+InetAddress.getLocalHost().toString();
					ps.println(info.append(userinfo));//输出信息
					ps.flush();
					listen = new Listen(this,tfName.getText(),socket);
					listen.start();
				}
			} else if (e.getSource() == btDisconnect) { //点击断开连接按钮
				disconnect();
			} else if (e.getSource() == btSend) { //点击发送按钮
				if (socket != null) {
					StringBuffer msg = new StringBuffer("MSG:");
					String msgtxt = new String(tfMessage.getText());
					ps.println(msg.append(msgtxt));//发送信息
					ps.flush();
				} else {
					JOptionPane.showMessageDialog(this, "请先连接!", "提示", 1);
				}
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();//输出错误信息
		}
	}
	public void disconnect() { //断开连接方法
		if (socket != null) {
			ps.println("quit");//发送信息
			ps.flush();
			
			socket = null;
			tfName.setText("");
		}
	}
	
	
	
	
	
	

	
	public ChatClient(Socket socket) {
		
		
		this.setLayout(new BorderLayout());
		
		JPanel panel1 = new JPanel();
		Label label = new Label("姓名");
		panel1.setBackground(Color.orange);
		panel1.add(label);
		panel1.add(tfName);
		panel1.add(btConnect);
		panel1.add(btDisconnect);
		this.add(panel1,BorderLayout.NORTH);
		
		JPanel panel2 = new JPanel();
		panel2.add(tfChat);
		panel2.add(list1);
		this.add(panel2,BorderLayout.CENTER);
		
		JPanel panel3 = new JPanel();
		Label label2 = new Label("聊天信息");
		panel3.add(label2);
		panel3.add(tfMessage);
		panel3.add(btSend);
		this.add(panel3,BorderLayout.SOUTH);
		
		this.setBounds(50,50,400,350);
		this.setVisible(true);
		
		btConnect.addActionListener(this);
		btDisconnect.addActionListener(this);
		btSend.addActionListener(this);
		
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ChatClient client = new ChatClient(new Socket());
		System.out.println(client.socket);
	}


在想做东西的时候,不能只拿着代码就想编程,先搞清楚原理规则,然后动手实践,否则事倍功半!了解规则,熟悉规则。
分享到:
评论
1 楼 salever 2011-09-15  
不错,貌似少一点超时设置

相关推荐

    基于改进YOLOv5s的森林烟火检测算法.pdf

    基于改进YOLOv5s的森林烟火检测算法.pdf

    人力资源管理工具绩效考核excel模板01.xlsx

    人力资源管理工具绩效考核excel模板01

    施工班组长绩效考核表.xls

    施工班组长绩效考核表

    57 -营业部经理绩效考核表1.xlsx

    57 -营业部经理绩效考核表1

    XX公司行政部绩效考核指标.xls

    XX公司行政部绩效考核指标

    ant-apache-xalan2-1.9.4-2.el7.x64-86.rpm.tar.gz

    1、文件内容:ant-apache-xalan2-1.9.4-2.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/ant-apache-xalan2-1.9.4-2.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    部门绩效考核表模板(基于KPI以月度为例2).xlsx

    部门绩效考核表模板(基于KPI以月度为例2)

    11-6-质检员绩效考核表(含自动计算、等级评价及任意设置等级).xlsx

    11-6-质检员绩效考核表(含自动计算、等级评价及任意设置等级)

    2024年最新全国河流、湖泊矢量数据(数据权威)

    2024最新全国河流湖泊矢量数据 【数据介绍】 2024年中国河流湖泊数据 一份包含中国境内所有主要河流和湖泊的地理信息数据。 数据格式:Shapefile:广泛使用的GIS数据格式,方便在各类GIS软件中使用。 数据获取:访问OpenStreetMap官网,通过导出工具选择中国区域并下载所需的数据。 使用Geofabrik等第三方网站,可以下载预处理好的中国区域的OSM数据。 数据使用:GIS软件:如QGIS、ArcGIS等,用户可以在这些软件中导入OSM数据进行可视化、分析和编辑。 数据应用: 环境研究:分析河流湖泊的水质变化,研究水资源分布及其环境影响。 城市规划:用于规划城市水系、洪水防控、水资源管理等。 导航和旅游:为河流湖泊的导航和旅游路线规划提供数据支持。 科研:为水文地理研究、生态保护、气候变化等领域提供基础数据。 数据特点: 实时更新:OSM数据由全球用户贡献,具有较高的实时性和更新频率。 开放性:所有数据都在开放许可下发布,允许用户自由使用、修改和分发。 详细性:由于全球志愿者的不断努力,数据细节较为丰富,涵盖了从主要河流湖泊到小型水体的广泛范围。 数据时间2024年5月,shp格式,数据来源OpenStreetMap。 OpenStreetMap(OSM)介绍: 一个开放的、免费的、全球性的地图项目,由全球的志愿者和地图爱好者们共同创建和维护。 OSM的数据包括道路、建筑、公园、河流、湖泊等各类地理信息。由于是由众多志愿者共同编辑,OSM的数据具有很高的实时性和详细程度,特别是在一些活跃的区域,地图数据的更新速度和精度往往超过商业地图服务。 用户可以直接在OSM官网下载地图数据,数据格式主要有OSM XML和PBF等。此外,还有一些第三方网站和工具提供更加便捷的数据下载和处理服务,如Geofabrik、Overpass API等。 OSM的数据可以在各种GIS软件中使用,如QGIS、ArcGIS等。此外,还可以使用Python的OSMnx、GeoPandas等库进行编程处理,或者通过Leaflet、Mapbox等JavaScript库将OSM数据集成到web地图应用中。 OSM的所有数据都在开放许可下发布,允许用户自由使用、修改和分发。这使得OSM成为了许多公共项目、研究机构和商业公司的重要数据来源。

    部门绩效考核评分表.xlsx

    部门绩效考核评分表

    12-11-运输车队长绩效考核表(含自动计算、等级评价).xlsx

    12-11-运输车队长绩效考核表(含自动计算、等级评价)

    ant-javadoc-1.9.4-2.el7.x64-86.rpm.tar.gz

    1、文件内容:ant-javadoc-1.9.4-2.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/ant-javadoc-1.9.4-2.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    springboot整合 freemarker方法

    springboot整合 freemarker方法

    apache-commons-codec-1.8-7.el7.x64-86.rpm.tar.gz

    1、文件内容:apache-commons-codec-1.8-7.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/apache-commons-codec-1.8-7.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    (数据权威)全国旅游抽样调查数据

    《旅游抽样调查资料》是反映入境游客在华(内地)花费和国内居民国内旅游情况的资料性年刊,分为上下两篇。 上篇为在华(内地)停留时间在3个月以内的入境游客抽样调查资料,由综合分析报告和调查分类数据两部分组成,分类数据包括:入境游客的主要特征,入境外国人、港澳台同胞的花费水平和花费构成、在境内的停留时间以及入境次数、流向和对住宿单位的选择等。 下篇为国内旅游抽样调查资料,汇集了对城镇居民和农村居民的国内旅游抽样调查结果,共分为四个部分:第一部分为综合分析报告;第二部分为国内旅游出游及花费情况;第三部分为城镇居民国内旅游抽样调查分类数据;第四部分为农村居民国内旅游抽样调查分类数据。

    二代身份证信息读取(vfp8.0)

    1、表单界面,身份证信息保存在dbf表中,供vfp应用使用,可导出为xls电子表格。 2、提供了身份证过期校验和查询功能。

    人事行政主管绩效考核评分表.xls

    人事行政主管绩效考核评分表

    08 -大堂副理绩效考核表1.xlsx

    08 -大堂副理绩效考核表1

    apr-1.4.8-7.el7.x64-86.rpm.tar.gz

    1、文件内容:apr-1.4.8-7.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/apr-1.4.8-7.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    ComponentNameError解决办法.md

    ComponentNameError解决办法.md

Global site tag (gtag.js) - Google Analytics