服务器和客户端的通信绘图

          还是先上图,现在的年轻人都不喜欢看文字,喜欢浏览图片,那就先把今天刚刚做完的一个东西先发出来,今天做完的“通信弹球”对于现在的我来说“现丑了”


服务器和客户端的通信绘图_第1张图片

                                                                图(a)
 


服务器和客户端的通信绘图_第2张图片

 

                                                         图(b)

图a是客户端的一个画板,图b是服务器端的一个画板,绘制在客户端的图像,通过一点点的协议和方法就能传到我们的服务器端,同时显示在服务器端的画板上,仔细看可能发现了(不仔细看也发现了)上面的图和下面的颜色有点不一样,这应该是延迟的原因,就像我们浏览网页时向服务器发送请求,要等半天才会有回应一样,就默认是这样的吧,在原谅的范围以内,图片也看了,下面开始今天的主题------通信。

         

        通信:通信,指人与人或人与自然之间通过某种行为或媒介进行的信息交流与传递,从广义上指需要信息的双方或多方在不违背各自意愿的情况下无论采用何种方法,使用何种媒质,将信息从某方准确安全传送到另方。(此处来自百度)

          上了这几节课,我对计算机上的通信的理解也就是:我有东西想给你,但是由于种种原因不能亲自给你,这可能有很多方面的原因,比如说是地域或者是不好意思,所以我要找一个东西来把我想给你的东西寄存在那里(在计算机上也就叫做服务器)如果你也想要知道给你的是什么东西!你就联系上它吧,但是,不是每个人平白无故的就能把我想给你的东西取走,就像寄快递的时候每个寄出的货物都有一个单号一样,服务器也是有号的要连接上他你就要知道他的单号是多少(也就是IP地址,和端口号),连接上后你就可以通过一点点的手段就可以把我的东西取走了。

          以上纯属个人见解,如有雷同不甚荣幸!

          怎么把东西放到寄存的那个地方呢?他又是怎么送到你的手上的呢?必须要有途径啊,就像快递通过海陆空送到各个地方一样,计算机中的信息也是一样,客户端要传到服务器里面,从服务器再传到客户端也是需要途径的,通信的“管道”,通过管道再加上一些通信的协议,就能实现数据的传输了,如下图所示:


服务器和客户端的通信绘图_第3张图片

 

                                                                               图(c)
 黑色的线就模拟了通信的通道,客户机和服务器连接,没个客户机对应着一套传输与接收的方法,进行数据的传输与共享。下面具体交代一下这几天学的东西。说说Java中是怎么实现服务器与客户端的。

 

在Java中要想编写网络通信,必须要用到java.net包下面的API,首先,我们来创建一个服务器,So easy  几行代码就可以搞定实例如下:

 

    Step1:

 

//创建一个服务器,并指定一个端口.

ServerSocket server = new ServerSocket(5678);

   PS:   端口号是什么?

               在网络技术中,端口(Port)大致有两种意思:一是物理意义上的端口,比如,ADSL Modem、集线器、交换机、路由器用 于连接其他网络设备的接口,如RJ-45端口、SC端口等等。二是逻辑意义上的端口,一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。我们这里将要介绍的就是逻辑意义上的端口。

             TCP与UDP段结构中端口地址都是16比特,可以有在0---65535范围内的端口号。对于这65536个端口号有以下的使用规定:

(1)端口号小于256的定义为常用端口,服务器一般都是通过常用端口号来识别的。任何TCP/IP实现所提供的服务都用1---1023之间的端口号,是由ICANN来管理的;
(2) 客户端只需保证该端口号在本机上是惟一的就可以了。 客户端口号因存在时间很短暂又称临时端口号;
(3)大多数TCP/IP实现给临时端口号分配1024---5000之间的端口号。大于5000的端口号是为其他服务器预留的
   所以,我们为我们自己写的服务器设置端口的时候应该避开前1024号端口.
Step2:服务器创建成功之后,就要让它进入等待状态,等待客户机的连接
/*客户机连接进入后,生成一个Socket对象,需要注意的是。调用了accept()方法
    *后,程序就会“阻塞”,就会等在这里,直到有一个客户机连接上来,这个方法才会返回,返回      * 的一个Socket对象就代表了服务器和客户机之间的连接,服务器和客户机上的通信就是在Socket     *对象client上进行
    /
    Socket client = server.accept();
 Step3:从Socket连接对象上调用方法得到输入输出流:
OutputStream out  = client.getOutputStream();
    InputStream  ins = client.getInputStream();
 Step4:使用输入输出流进行通信数据的读写,从输入流中读取从客户端发来的数据,在输出流写入数据   传送到客户端,这里需要注意的是不同类型的数据传输的机制是不同的入字符串就要先取得字符串的字节。
通过上面的四步一个简单的服务器就创建好了。真的不是很难。多练练那几行代码就能背下来了。
 
我们使用While()循环就能连接进来很多客户机
但是上面的服务器只能连接一个客户机,前一个退出后下一个才能接进来,原因就是调用accept()方法时卡卡住了,要等到第一个客户机执行完下面所有的事情之后,才能再次调用accept()方法,所以聪明的你应该想到了线程这个好东西,将进入服务器的每一个Socket对象交给一个线程去处理,接下来想要来接入的客户端就不需要等待了,直接开启下一个线程。然后用一个While(true)死循环来循环调用start()方法即可。
 
下面来说一下java中客户端的编写,一行代码,只要知道服务器的Ip地址和端口号示例如下:
//连接服务器
Socket socket = new Socket(ip,port);
 
但是要完成从服务器端的数据读写就要多写几行代码了
private DataInputStream dins;
    private DataOutputStream dous;	

         /**
	 * 连接服务器的方法
	 * @param ip 客户端的Ip地址
	 * @param port 服务器的端口号
	 * @return 成功返回true,失败返回false
	 */
	public boolean connServer(String ip,int port) {
		try {
			
			//连接服务器
			Socket socket = new Socket(ip,port);
			
			//得到输入输出流
			InputStream ins = socket.getInputStream();
			OutputStream ous = socket.getOutputStream();
			
			//读写通信数据
			dins = new DataInputStream(ins);
			dous = new DataOutputStream(ous);
			
		}catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
        
    //从服务器上读取数据,以读取整形数据为例
	public void readFromServer() {
		while(true) {
			try {
				int x = dins.readInt();
				int y = dins.readInt();
			} catch (IOException e) {
				e.printStackTrace();
			}
		
		}
	}

    //向服务器发送数据以传输整形数据为例:
     public void sendXY(int x,int y) {
           try {
			dous.writeInt(x);
			dous.writeInt(y);
		} catch (Exception e) {
			e.printStackTrace();
		}
}

 

    最后把刚刚做完的一个小实验奉上,基于通信的”弹球“,主要的目的是练习服务器端界面编程和客户端的界面编程方法,还有数据的传递,熟练掌握后对后面一些基于通信的小游戏的开发应该有很大的帮助,自己做的游戏不再是单机版的了,也同时可以和小伙伴们一起玩耍。
 小实验一共为两个项目,一个是客户端,一个是服务器端,首先启动服务器,再启动客户端进行连接。

服务器和客户端的通信绘图_第4张图片
 
想要达到的效果就是小球能够实现在客户端和服务器端的同时弹动,代码很简单把主要的贴上。
客户端连接服务器并进行数据的传输的代码如上,下面贴上客户端控制小球运动和数据传输的代码:
package Tms.netjava.com;

import java.awt.Color;
import java.awt.Graphics;
/**
 * 客户端画小球的线程
 * 
 * @author sony
 * 
 */
public class DrawThread extends Thread {

	private Graphics g;
	private NetConn nc;
	//小球的初始坐标位置
	int x=200 ;
	int y=300 ;
       //设置小球的初试半径,后面碰到四周后,会越来越大,当大到一定程度时也可以缩小(没做)
	int rd = 20;
	//运动的速度为(1/36)秒每贞,看上去移动的比较平缓
	int speed = 1; 
	int red = 255;
	int green = 1;
	int blue = 255;
	//小球初始角度,是和正上方的夹角的大小
	private int angle=30;

	// 得到画布的高和宽

	public DrawThread(Graphics g) {
		this.g = g;
		nc = new NetConn();
		if (nc.connServer("192.168.56.1", 9090)) {
			// 读取数据

			nc.start();

		}
	}

	public void run() {

		while(true) {
			move();
			try {
				Thread.sleep(1000/36);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 小球运动的方法
	 */
	public void move() {
		
		//当小球碰到四周时,角度相应的变化,窗体的初始高和宽是(600,400)
		if(x<0||x>=600) {
			angle = 360 - angle;
			//碰到壁后改变小球半径
                         rd+=10;
		}
		if(y<0||y>=400) {
			angle = 180 - angle;
			rd+=10;
		}
		
		//测试代码
		System.out.println("x==="+x);
	    System.out.println("y==="+y);
	 
		double x11 = speed*Math.sin(angle*Math.PI/180);
	
		double y11 = speed*Math.cos(angle*Math.PI/180);
	
	   //因为计算的值为-1到1之间的小数,调用相应的向下floor()取整,和向上ceil取整方法
	   //并对计算值的正负进行判断,进行相应的处理,纸上画画就明白了。
		if(x11<0) {
			x += Math.floor(speed*Math.sin(angle*Math.PI/180));
		} else{
			x += Math.ceil(speed*Math.sin(angle*Math.PI/180));
		}
		
		if(y11<0) {
		
			y -=-Math.ceil(-y11);
		} else{
			y -=Math.ceil(speed*Math.cos(angle*Math.PI/180));
			
		}
		
		//给背景设置变化的颜色,也是清屏的颜色,变换的,好看一点
		
		red-=2;
		if(red<=0) {
			red=255;	
		}
		green+=5;
		if(green>=0) {
			green=255;	
		}
		blue-=7;
		if(blue<=0) {
			blue=255;
		}
		
		//将 数据传到服务器端
		nc.sendData(x, y,red,green,blue,rd);
		
		g.setColor(new Color(red,green,blue));
		g.fillRect(0, 0, 600, 400);
		//滚动的小球的颜色也随之改变
		g.setColor(new Color(green,blue,red));
		g.fillOval(x, y, rd, rd);
		
	}
}
   下面贴上服务器端的主要代码:
package tms.netjava.com;

import java.awt.Color;
import java.awt.Graphics;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TmsServer extends Thread {

	// 创建画布
	private Graphics g;

	public TmsServer(Graphics g) {
		this.g = g;
	}

	public void run() {
		this.startServer(5678);
	}

	public void startServer(int port) {
		try {

			System.out.println("创建服务器在端口:" + port);
			// 创建服务器
			ServerSocket ss = new ServerSocket(port);
			// 等待客户机的链接
			Socket client = ss.accept();

			// 打印客户机的地址
			System.out.println("连接进来一个客户机,客户机的地址为"
					+ client.getRemoteSocketAddress());

			// 取得输入输出流
			InputStream ins = client.getInputStream();
			OutputStream ous = client.getOutputStream();

			// 读写通信数据
			DataInputStream dins = new DataInputStream(ins);
			DataOutputStream dous = new       DataOutputStream(ous);

			// 从客户端得到发来的数据
			while (true) {
				int x = dins.readInt();
				int y = dins.readInt();
				int red = dins.readInt();
				int green = dins.readInt();
				int blue = dins.readInt();
				int rd = dins.readInt();

				// 清屏
				g.setColor(new Color(red, green, blue));
				g.fill3DRect(5, 5, 600, 400, false);

				// 利用得到坐标将指定的图形画出来
				g.setColor(new Color(green, blue, red));
				g.fillOval(x, y, rd, rd);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
  
实现的效果也就是小球会在客户端的画布和服务器端的画布上变换着颜色弹动,越弹越大。
效果如下:
 

服务器和客户端的通信绘图_第5张图片
 
服务器和客户端的通信绘图_第6张图片
 
撞了几次墙之后:

服务器和客户端的通信绘图_第7张图片
 
服务器和客户端的通信绘图_第8张图片
 

服务器和客户端的通信绘图_第9张图片
 
 
服务器和客户端的通信绘图_第10张图片
 

最后总结:踏踏实实,一步一个脚印的慢慢的踩。即使路上布满荆棘,也要勇敢走下去,不管结果如何,吃多了还是会长胖。
 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Java基础)