欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

socket通信

程序员文章站 2024-01-31 20:26:10
...
      前段时间学习了通信机制,小组做了一个仿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);
}

测试截图:
socket通信
            
    
    博客分类: JAVA基础 java通信机制socket通信 
  • socket通信
            
    
    博客分类: JAVA基础 java通信机制socket通信 
  • 大小: 173.9 KB