`
hmeng
  • 浏览: 15201 次
  • 性别: Icon_minigender_2
社区版块
存档分类
最新评论

socket通信

阅读更多
      前段时间学习了通信机制,小组做了一个仿QQ的聊天工具,能登录,注册,加好友,私聊,群聊,能玩通信游戏。我完成的任务一个你画我猜的通信游戏的模块,并完成了测试。
     本文主要叙述socket的通信机制,关于你画我猜这个模块后续再写出来,这里贴的代码是我完成的模块代码中截取出来的,只为了体现逻辑思路,连贯性可能欠缺请见谅。
     我们在局域网下进行socket通信,首先建立一个本机的服务器,监听端口,等待访问,当有客户端访问时,交给服务端线程处理;然后当客户端对象访问服务器时,客户端线程处理客户端的操作,通过TCP/ip协议与服务端通信。

     下面先介绍一些术语:
     TCP/IP协议是一种面向连接的,可靠的网络传输协议,比如你给别人打电话,必须等线路接通了、对方拿起话筒才能相互通话。而UDP协议是非面向连接的协议,就是在正式通信前不必与对方先建立连接,例如你在发短信的时候,只需要输入对方手机号就OK了。
      一个TCP连接必须要经过三次“对话”才能建立起来,这三次对话的简单过程:主机A向主机B发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;主机B向主机A发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;主机A再发出一个数据包确认主机B的要求同步:“我现在就发,你接着吧!”,这是第三次对话。三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机A才向主机B正式发送数据。
      我们要确认网络上的每一台计算机,靠的就是能唯一标识该计算机的网络地址,这个地址就叫做IP。在Internet里,IP地址是一个32位的二进制地址,为了便于记忆,将它们分为4组,每组8位,由小数点分开,用四个字节来表示,而且,用点分开的每个字节的数值范围是0~255,如202.116.0.1。
       socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。

      接下来用代码进行说明:
     1、服务端:首先建立一个ServerSocket服务器端口号,当有客户端访问时,交给服务端线程处理。

public class Server {//服务端
	public void setup(int port){
	    try {
	    	//绑定服务器端口号
			ServerSocket sers = new ServerSocket(port);
			System.out.println("服务器监听端口"+port+"成功!");
			
			while(true){
				//等待客户端访问
				Socket socket = sers.accept();
				System.out.println("有人访问!");
				
				//把客户端交给线程处理
				SocketThread st = new SocketThread(socket);
				st.start();
			}			
		} catch (IOException e) {
			e.printStackTrace();
		}		
	}	
	public static void main(String[] args) {
		new Server().setup(6666);

	}

    服务端线程:得到连接入socket的一个输入输出流,Output写数据送到客户机,Input读取数据到服务器,自定义通信协议,根据接受数据包的不同类型进行不同处理。线程类中的run方法以及部分想客户端发送消息的方法如下,更多的向客户端发送不同类型数据的方法不再赘述:
public void run() {
		try {
			// 读取客户端发送的消息

			input = socket.getInputStream();

			DataInputStream dis = new DataInputStream(input);
			// 向客户端发送的消息
			output = socket.getOutputStream();
			dos = new DataOutputStream(output);
			// 输入名字
			String str = "请输入你的名字:\r\n";
			// 服务器向客户端发送消息
			sendMessage(str);
			// 读取客户端输入的名字
			name = readLine(input);
			String name2 = name + "(" + socket.getInetAddress() + ")";
			System.out.println("name:" + name2);
			// 从客户端读取字符串消息
			while (true) {
				// 接受数据包的类型
				int type = dis.readInt();
				if (type == 1) {
					// 接收数据包的长度
					int len = dis.readInt();
					byte[] bytes = new byte[len];
					dis.readFully(bytes);
					// 读取客户端的输入流字符串
					String line = new String(bytes, "GBK");

					if ("bye\n".equals(line)) {
						System.out.println("服务器收到 " + name + "已下线!");
						break;
					}
					// 打印当前客户所说的话
					System.out.println("服务器收到 " + name + ":" + line + "======");
                    System.out.println(keyWord+"\n======");
					if (line.equals(keyWord + "\n")) {//判断对方是否猜对
						System.out.println("猜对了!");
						for (int i = 0; i < list.size(); i++) {
							SocketThread st = list.get(i);
							// 向其他客户端发出消息
							st.sendMessage(name + "猜对了!\n");
							st.sendAccess("good");
						}
						sendTitle();

					}

					// 群发消息
					for (int i = 0; i < list.size(); i++) {
						SocketThread st = list.get(i);
						if (st == this) {
							continue;
						}
						// 向其他客户端发出消息
						String msg = name + ":" + line;
						st.sendMessage(msg);

					}
				} else if (type == 2) {
					// 接收画图信息
					int len = dis.readInt();
					int x1 = dis.readInt();
					int y1 = dis.readInt();
					int x2 = dis.readInt();
					int y2 = dis.readInt();
					int r = dis.readInt();
					int g = dis.readInt();
					int b = dis.readInt();
					Color c = new Color(r,g,b);
					// 群发消息
					for (int i = 0; i < list.size(); i++) {
						// System.out.println("群发");
						SocketThread st = list.get(i);
						// 向其他客户端发出画图消息
						st.sendDraw(x1, y1, x2, y2,c);

					}
				} else if (type == 3) {
					if (list.size() <= 1) {
						return;
					}
					// 发送题目
					sendTitle();
				} else if (type == 4) {
					// 群发清屏消息
					for (int i = 0; i < list.size(); i++) {
						SocketThread st = list.get(i);
						// 向所有客户端发出画图消息
						st.sendClear();
					}
				} else if (type == 5) {
					// 接收数据包的长度
					int len = dis.readInt();
					byte[] bytes = new byte[len];
					dis.readFully(bytes);
					// 读取客户端的输入流字符串
					String line = new String(bytes, "GBK");
					// 给画图者发送评价消息
					drawst.sendAccess(line);

				}
			}
			// 客户下线关闭当前端口
			socket.close();
			// 删除队列中的对象
			list.remove(this);

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

/*
	 * 读取输入流的方法
	 */
	private String readLine(InputStream input) throws IOException {
		// 新建一个字节队列
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataInputStream dis = new DataInputStream(input);
		dis.readInt();
		dis.readInt();
		while (true) {
			int n = input.read();
			// System.out.println(n);
			// 回车符
			if (n == '\r') {
				continue;
			}
			// 换行符
			if (n == '\n') {
				break;
			}
			// 把读取的字节内容先保存
			bos.write(n);
		}
		// 把字节队列中的数据取出来
		byte[] bytes = bos.toByteArray();
		String content = new String(bytes, "GBK");
		return content;
	}

	/*
	 * 向客户端发送消息的方法
	 */
	public void sendMessage(String msg) {
		try {
			// 服务器输出流写入字节
			byte[] bytes = msg.getBytes();
			int len = bytes.length;
			dos.writeInt(1);
			dos.writeInt(len);
			dos.write(bytes);
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * 向客户端发送画图线段消息的方法
	 */
	public void sendDraw(int x1,int y1,int x2,int y2,Color color){
		try {
			//客户端输出流写入字节	
			dos.writeInt(2);
			dos.writeInt(28);
			dos.writeInt(x1);
			dos.writeInt(y1);
			dos.writeInt(x2);
			dos.writeInt(y2);
			int red = color.getRed();
			int green = color.getGreen();
			int blue = color.getBlue();
			dos.writeInt(red);
			dos.writeInt(green);
			dos.writeInt(blue);
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

     2、客户端:创建一个客户端窗体,并初始化界面,然后启动客户端线程处理。这部分比较简单不贴代码。
       客户端线程:创建对应服务端的socket套接字,连接服务器,根据通信协议,依不同的数据包类型进行处理,run方法如下

public void run (){
		try{
			System.out.println("连接服务器......");
			Socket socket = new Socket("127.0.0.1",6666);
			System.out.println("成功!");
			//读取对方发送的消息
			InputStream input = socket.getInputStream();
			DataInputStream dis = new DataInputStream(input);
			//向对方发送的消息
			output = socket.getOutputStream();
			dos = new DataOutputStream(output);			
			while(true){
				//接受数据包的类型
				int type=dis.readInt();
				if(type==1){
					//接收数据包的长度
					int len = dis.readInt();
					byte[] bytes= new byte[len];
					dis.readFully(bytes);
					//读取客户端的输入流字符串
					String line = new String(bytes,"GBK");
//					System.out.println(line);
					//從服务器接收到的消息显示到界面上
					l.onRecvMsg(line);

				}else if(type == 2){
					//接收画图信息
					int len = dis.readInt();
					int x1 = dis.readInt();
					int y1 = dis.readInt();
					int x2 = dis.readInt();
					int y2 = dis.readInt();
					int r = dis.readInt();
					int g = dis.readInt();
					int b = dis.readInt();
					Color c = new Color(r,g,b);
					l.onDraw(x1, y1, x2, y2,c);
				}else if(type==3){
					//从服务端接受画图题目信息
					int len = dis.readInt();
					byte[] bytes= new byte[len];
					dis.readFully(bytes);
					//读取客户端的输入流字符串
					String str = new String(bytes,"GBK");
					l.onTitle(str);
				}else if(type == 4){
					//清屏
					l.onclear();
				}else if (type == 5) {
					// 接收数据包的长度
					int len = dis.readInt();
					byte[] bytes = new byte[len];
					dis.readFully(bytes);
					// 读取客户端的输入流字符串
					String line = new String(bytes, "GBK");
					// 给画图者增加评价消息
					l.onAccess(line);

				}
				
				
			}
		}catch (Exception e){
			System.out.println("失败!");
			e.printStackTrace();
		}
	}
其中在接受到服务端数据之后,对客户端界面处理的方法通过客户端类实现接口MsgListener来完成,这样做能使代码的设计更合理,模块之间调用更加方便。
public interface MsgListener {
	//在日志上显示消息
	public void onRecvMsg(String str);
	//在画图区上画图
	public void onDraw(int x1,int y1,int x2,int y2,Color color);
	//显示题目
	public void onTitle(String str);
	//清屏
	public void onclear();
	//显示评价
	public void onAccess(String access);
}

测试截图:

  • 大小: 173.9 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics