`
Coco_young
  • 浏览: 120657 次
  • 性别: Icon_minigender_1
  • 来自: 湖南长沙
社区版块
存档分类
最新评论

初识通信——多线程服务器的建立

阅读更多
前段时间悲剧的生病了,所以通信阶段的学习也就停了下来,这段时间身体见好,所以在家看了看书。虽然生病前已经写好过一个多线程服务器,但是来不及总结,整体结构也相当混乱,所以在此重新写了一遍,也算是复习一下。


package Practice1_ThreadServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * 服务器端的线程类,负责处理客户端
 * @author yfr
 *
 */
public class ServerThread extends Thread {

	private Socket client;//客户端
	private OutputStream ous;//输出流
	private InputStream ins;//输入流
	
	/**
	 * 构造函数
	 * @param client :传入客户端对象
	 */
	public ServerThread(Socket client){
		this.client = client;
		try {
			ous = this.client.getOutputStream();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 传送消息给客户端
	 * @param msg: 消息
	 */
	private void sendMsg2Client(String msg){
		
		byte[] data = msg.getBytes();
		try {
			ous.write(data);
			ous.flush();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 读取字符串的方法
	 * @return 读取的字符串
	 * @throws Exception 
	 */
	private String readString() throws Exception{
		
		//获得输入流
	    ins = client.getInputStream();
		//创建字符串缓冲区
		StringBuffer stb = new StringBuffer();
		
		int data = 0;
		int EXIT = 13;//当读到回车就表示已经读到了一个字符串
		
		while(data!=EXIT){
			
			data = ins.read();
			
			stb.append((char)data);
			
		}
		
		return stb.toString().trim();
	}
	
	/**
	 * 处理客户端的方法
	 */
	private void ProcessClient(){
		
		//发送欢迎消息给客户端
		String WelComeMsg = "你好,欢迎来到老杨的服务器\r\n";
		sendMsg2Client(WelComeMsg);
		
		String ClientMsg = "";
		String EXIT = "bye";
		
		//当读到的消息不是bye时 表明聊天还没结束
		while(!ClientMsg.equals(EXIT)){
			try {
				ClientMsg = readString();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			String tempMsg = "服务器收到:"+ClientMsg+"\r\n";
			sendMsg2Client(tempMsg);
		}
		
		//发送再见消息
		String BYBYEMsg = "期待下次光临\r\n";
		sendMsg2Client(BYBYEMsg);
		
		//关闭与服务器的连接
		try {
			client.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 线程运行
	 */
	public void run(){
		ProcessClient();
	}
	
	
	
}


package Practice1_ThreadServer;
import java.net.ServerSocket;

/***
 * 服务器主类
 * @author yfr
 *
 */
public class ServerMain {

	/**
	 * 主函数
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		
		ServerMain sm = new ServerMain();
		sm.setUpServer(9090);

	}
	
	/**
	 * 创建服务器
	 * @param port :端口号
	 * @throws Exception 
	 */
	private void setUpServer(int port) throws Exception{
		//创建服务器对象
		java.net.ServerSocket server = new ServerSocket(port);
		
		boolean  Working = true;//服务器的工作状态
		
		while(Working){
			
			//循环等待接收客户端对象
			java.net.Socket client = server.accept();
			
			//创建一个线程对象
			ServerThread dealMachine = new ServerThread(client);
			
			//启用线程单独去处理
			dealMachine.start();
			
		}
		
	}

}





编写完这个多线程服务器后,小结如下几点:
1.正确使用ServerSocket类建立服务器,并接收Socket对象。
2.使用telnet网络命令,连接上服务器。
3.理解Socket对象的输入输出流,并能使用它们在服务器和客户端之间实现简单信息的传输。
4.利用Thread实现多个客户端同时连接上服务器



初识通信,印象深刻只有10个字“服务器”,“客户端”,“数据传输”。



另外一点,是关于StringBuffer 和 String 的一点讨论:


/**
	 * 读取字符串的方法
	 * @return 读取的字符串
	 * @throws Exception 
	 */
	private String readString() throws Exception{
		
		//获得输入流
	    ins = client.getInputStream();
		//创建字符串缓冲区
		StringBuffer stb = new StringBuffer();
		
		int data = 0;
		int EXIT = 13;//当读到回车就表示已经读到了一个字符串
		
		while(data!=EXIT){
			
			data = ins.read();
			
			stb.append((char)data);
			
		}
		
		return stb.toString().trim();
	}


如果把这段代码改成:
/**
	 * 读取字符串的方法
	 * @return 读取的字符串
	 * @throws Exception 
	 */
	private String readString() throws Exception{
		
		//获得输入流
	    ins = client.getInputStream();
		//创建字符串缓冲区
		String stb = "";
		
		int data = 0;
		int EXIT = 13;//当读到回车就表示已经读到了一个字符串
		
		while(data!=EXIT){
			
			data = ins.read();
			
			stb+=(char)data;
			
		}
		
		return stb.trim();
	}
	

是否具有同样的效果呢?开始我也想了很久,后来查资料发现,表面上是一样的效果,但是效率确是不一样的,引用人家的解释如下:
首先,
public class xx {public static void main(String[] args) { String s1 = "You are hired!";String s2 = "You are hired!";if (s1==s2) {System.out.println("一个内存空间");} else {System.out.println("不是一个内存空间");}}}
打印的结果是:一个内存空间
这里==的意义是两个操作数是否指向同一个对象
可见s2在不用new创建的情况下会自动检索到具有相同内容的内存空间中共享,那么既然s1和s2共享了同一个对象
再看下面的代码


public class xx {public static void main(String[] args) { String s1 = "You are hired!";String s2 = "You are hired!";s1 = s1.replace('h','f');System.out.println(s1);if (s1==s2) {System.out.println("一个内存空间");} else {System.out.println("不是一个内存空间");}}}
代码结果是
You are fired!
不是一个内存空间
可见,String中s1的内容虽然被改写,但是已经不在是原来第一次分配到的那个内存空间,也就是String类的内容能被改变,但一旦改变系统将为其分配新的内存
说到与stringBuffer的区别,从根本上来说应该是
stringBuffer在做字符长度变动的时候将继续使用原来的内存空间,不新分配.
而String的长度一旦变动,就如上面的例子一样,其内部将分配新的内存空间.  

在java中有3个类来负责字符的操作。
1.Character 是进行单个字符操作的,
2.String 对一串字符进行操作。不可变类。
3.StringBuffer 也是对一串字符进行操作,但是可变类。
String:
是对象不是原始类型.
为不可变对象,一旦被创建,就不能修改它的值.
对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.
String 是final类,即不能被继承.
StringBuffer:
是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象
它只能通过构造函数来建立,
StringBuffer sb = new StringBuffer();
note:不能通过付值符号对他进行付值.
sb = "welcome to here!";//error
对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer
中付值的时候可以通过它的append方法.
sb.append("hello");
字符串连接操作中StringBuffer的效率要比String高:
String str = new String("welcome to ");
str += "here";
的处理步骤实际上是通过建立一个StringBuffer,让侯调用append(),最后
再将StringBuffer toSting();
这样的话String的连接操作就比StringBuffer多出了一些附加操作,当然效率上要打折扣.
并且由于String 对象是不可变对象,每次操作Sting 都会重新建立新的对象来保存新的值.
这样原来的对象就没用了,就要被垃圾回收.这也是要影响性能的.


由此可见,当需要频繁的对字符串进行修改连接操作时,选用StringBuffer的效率会比String高很多。至此终于理解为什么用StringBuffer。最后再上传一份关于StringBuffer StringBuilder String的详细讨论(作者不是本人),有兴趣的可以下载看看。
2
2
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics