socket通信

来源:互联网 发布:小脑袋调价软件 编辑:程序博客网 时间:2024/05/26 12:01
前段时间学习了通信机制,小组做了一个仿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服务器端口号,当有客户端访问时,交给服务端线程处理。
 
Java代码  收藏代码
  1. public class Server {//服务端  
  2.     public void setup(int port){  
  3.         try {  
  4.             //绑定服务器端口号  
  5.             ServerSocket sers = new ServerSocket(port);  
  6.             System.out.println("服务器监听端口"+port+"成功!");  
  7.               
  8.             while(true){  
  9.                 //等待客户端访问  
  10.                 Socket socket = sers.accept();  
  11.                 System.out.println("有人访问!");  
  12.                   
  13.                 //把客户端交给线程处理  
  14.                 SocketThread st = new SocketThread(socket);  
  15.                 st.start();  
  16.             }             
  17.         } catch (IOException e) {  
  18.             e.printStackTrace();  
  19.         }         
  20.     }     
  21.     public static void main(String[] args) {  
  22.         new Server().setup(6666);  
  23.   
  24.     }  

    服务端线程:得到连接入socket的一个输入输出流,Output写数据送到客户机,Input读取数据到服务器,自定义通信协议,根据接受数据包的不同类型进行不同处理。线程类中的run方法以及部分想客户端发送消息的方法如下,更多的向客户端发送不同类型数据的方法不再赘述: 
Java代码  收藏代码
  1. public void run() {  
  2.         try {  
  3.             // 读取客户端发送的消息  
  4.   
  5.             input = socket.getInputStream();  
  6.   
  7.             DataInputStream dis = new DataInputStream(input);  
  8.             // 向客户端发送的消息  
  9.             output = socket.getOutputStream();  
  10.             dos = new DataOutputStream(output);  
  11.             // 输入名字  
  12.             String str = "请输入你的名字:\r\n";  
  13.             // 服务器向客户端发送消息  
  14.             sendMessage(str);  
  15.             // 读取客户端输入的名字  
  16.             name = readLine(input);  
  17.             String name2 = name + "(" + socket.getInetAddress() + ")";  
  18.             System.out.println("name:" + name2);  
  19.             // 从客户端读取字符串消息  
  20.             while (true) {  
  21.                 // 接受数据包的类型  
  22.                 int type = dis.readInt();  
  23.                 if (type == 1) {  
  24.                     // 接收数据包的长度  
  25.                     int len = dis.readInt();  
  26.                     byte[] bytes = new byte[len];  
  27.                     dis.readFully(bytes);  
  28.                     // 读取客户端的输入流字符串  
  29.                     String line = new String(bytes, "GBK");  
  30.   
  31.                     if ("bye\n".equals(line)) {  
  32.                         System.out.println("服务器收到 " + name + "已下线!");  
  33.                         break;  
  34.                     }  
  35.                     // 打印当前客户所说的话  
  36.                     System.out.println("服务器收到 " + name + ":" + line + "======");  
  37.                     System.out.println(keyWord+"\n======");  
  38.                     if (line.equals(keyWord + "\n")) {//判断对方是否猜对  
  39.                         System.out.println("猜对了!");  
  40.                         for (int i = 0; i < list.size(); i++) {  
  41.                             SocketThread st = list.get(i);  
  42.                             // 向其他客户端发出消息  
  43.                             st.sendMessage(name + "猜对了!\n");  
  44.                             st.sendAccess("good");  
  45.                         }  
  46.                         sendTitle();  
  47.   
  48.                     }  
  49.   
  50.                     // 群发消息  
  51.                     for (int i = 0; i < list.size(); i++) {  
  52.                         SocketThread st = list.get(i);  
  53.                         if (st == this) {  
  54.                             continue;  
  55.                         }  
  56.                         // 向其他客户端发出消息  
  57.                         String msg = name + ":" + line;  
  58.                         st.sendMessage(msg);  
  59.   
  60.                     }  
  61.                 } else if (type == 2) {  
  62.                     // 接收画图信息  
  63.                     int len = dis.readInt();  
  64.                     int x1 = dis.readInt();  
  65.                     int y1 = dis.readInt();  
  66.                     int x2 = dis.readInt();  
  67.                     int y2 = dis.readInt();  
  68.                     int r = dis.readInt();  
  69.                     int g = dis.readInt();  
  70.                     int b = dis.readInt();  
  71.                     Color c = new Color(r,g,b);  
  72.                     // 群发消息  
  73.                     for (int i = 0; i < list.size(); i++) {  
  74.                         // System.out.println("群发");  
  75.                         SocketThread st = list.get(i);  
  76.                         // 向其他客户端发出画图消息  
  77.                         st.sendDraw(x1, y1, x2, y2,c);  
  78.   
  79.                     }  
  80.                 } else if (type == 3) {  
  81.                     if (list.size() <= 1) {  
  82.                         return;  
  83.                     }  
  84.                     // 发送题目  
  85.                     sendTitle();  
  86.                 } else if (type == 4) {  
  87.                     // 群发清屏消息  
  88.                     for (int i = 0; i < list.size(); i++) {  
  89.                         SocketThread st = list.get(i);  
  90.                         // 向所有客户端发出画图消息  
  91.                         st.sendClear();  
  92.                     }  
  93.                 } else if (type == 5) {  
  94.                     // 接收数据包的长度  
  95.                     int len = dis.readInt();  
  96.                     byte[] bytes = new byte[len];  
  97.                     dis.readFully(bytes);  
  98.                     // 读取客户端的输入流字符串  
  99.                     String line = new String(bytes, "GBK");  
  100.                     // 给画图者发送评价消息  
  101.                     drawst.sendAccess(line);  
  102.   
  103.                 }  
  104.             }  
  105.             // 客户下线关闭当前端口  
  106.             socket.close();  
  107.             // 删除队列中的对象  
  108.             list.remove(this);  
  109.   
  110.         } catch (IOException e) {  
  111.             e.printStackTrace();  
  112.         }  
  113.   
  114.     }  

Java代码  收藏代码
  1. /* 
  2.      * 读取输入流的方法 
  3.      */  
  4.     private String readLine(InputStream input) throws IOException {  
  5.         // 新建一个字节队列  
  6.         ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  7.         DataInputStream dis = new DataInputStream(input);  
  8.         dis.readInt();  
  9.         dis.readInt();  
  10.         while (true) {  
  11.             int n = input.read();  
  12.             // System.out.println(n);  
  13.             // 回车符  
  14.             if (n == '\r') {  
  15.                 continue;  
  16.             }  
  17.             // 换行符  
  18.             if (n == '\n') {  
  19.                 break;  
  20.             }  
  21.             // 把读取的字节内容先保存  
  22.             bos.write(n);  
  23.         }  
  24.         // 把字节队列中的数据取出来  
  25.         byte[] bytes = bos.toByteArray();  
  26.         String content = new String(bytes, "GBK");  
  27.         return content;  
  28.     }  
  29.   
  30.     /* 
  31.      * 向客户端发送消息的方法 
  32.      */  
  33.     public void sendMessage(String msg) {  
  34.         try {  
  35.             // 服务器输出流写入字节  
  36.             byte[] bytes = msg.getBytes();  
  37.             int len = bytes.length;  
  38.             dos.writeInt(1);  
  39.             dos.writeInt(len);  
  40.             dos.write(bytes);  
  41.             dos.flush();  
  42.         } catch (IOException e) {  
  43.             e.printStackTrace();  
  44.         }  
  45.     }  
  46.   
  47.     /* 
  48.      * 向客户端发送画图线段消息的方法 
  49.      */  
  50.     public void sendDraw(int x1,int y1,int x2,int y2,Color color){  
  51.         try {  
  52.             //客户端输出流写入字节      
  53.             dos.writeInt(2);  
  54.             dos.writeInt(28);  
  55.             dos.writeInt(x1);  
  56.             dos.writeInt(y1);  
  57.             dos.writeInt(x2);  
  58.             dos.writeInt(y2);  
  59.             int red = color.getRed();  
  60.             int green = color.getGreen();  
  61.             int blue = color.getBlue();  
  62.             dos.writeInt(red);  
  63.             dos.writeInt(green);  
  64.             dos.writeInt(blue);  
  65.             dos.flush();  
  66.         } catch (IOException e) {  
  67.             e.printStackTrace();  
  68.         }  
  69.     }  

     2、客户端:创建一个客户端窗体,并初始化界面,然后启动客户端线程处理。这部分比较简单不贴代码。 
       客户端线程:创建对应服务端的socket套接字,连接服务器,根据通信协议,依不同的数据包类型进行处理,run方法如下
 
Java代码  收藏代码
  1. public void run (){  
  2.         try{  
  3.             System.out.println("连接服务器......");  
  4.             Socket socket = new Socket("127.0.0.1",6666);  
  5.             System.out.println("成功!");  
  6.             //读取对方发送的消息  
  7.             InputStream input = socket.getInputStream();  
  8.             DataInputStream dis = new DataInputStream(input);  
  9.             //向对方发送的消息  
  10.             output = socket.getOutputStream();  
  11.             dos = new DataOutputStream(output);           
  12.             while(true){  
  13.                 //接受数据包的类型  
  14.                 int type=dis.readInt();  
  15.                 if(type==1){  
  16.                     //接收数据包的长度  
  17.                     int len = dis.readInt();  
  18.                     byte[] bytes= new byte[len];  
  19.                     dis.readFully(bytes);  
  20.                     //读取客户端的输入流字符串  
  21.                     String line = new String(bytes,"GBK");  
  22. //                  System.out.println(line);  
  23.                     //從服务器接收到的消息显示到界面上  
  24.                     l.onRecvMsg(line);  
  25.   
  26.                 }else if(type == 2){  
  27.                     //接收画图信息  
  28.                     int len = dis.readInt();  
  29.                     int x1 = dis.readInt();  
  30.                     int y1 = dis.readInt();  
  31.                     int x2 = dis.readInt();  
  32.                     int y2 = dis.readInt();  
  33.                     int r = dis.readInt();  
  34.                     int g = dis.readInt();  
  35.                     int b = dis.readInt();  
  36.                     Color c = new Color(r,g,b);  
  37.                     l.onDraw(x1, y1, x2, y2,c);  
  38.                 }else if(type==3){  
  39.                     //从服务端接受画图题目信息  
  40.                     int len = dis.readInt();  
  41.                     byte[] bytes= new byte[len];  
  42.                     dis.readFully(bytes);  
  43.                     //读取客户端的输入流字符串  
  44.                     String str = new String(bytes,"GBK");  
  45.                     l.onTitle(str);  
  46.                 }else if(type == 4){  
  47.                     //清屏  
  48.                     l.onclear();  
  49.                 }else if (type == 5) {  
  50.                     // 接收数据包的长度  
  51.                     int len = dis.readInt();  
  52.                     byte[] bytes = new byte[len];  
  53.                     dis.readFully(bytes);  
  54.                     // 读取客户端的输入流字符串  
  55.                     String line = new String(bytes, "GBK");  
  56.                     // 给画图者增加评价消息  
  57.                     l.onAccess(line);  
  58.   
  59.                 }  
  60.                   
  61.                   
  62.             }  
  63.         }catch (Exception e){  
  64.             System.out.println("失败!");  
  65.             e.printStackTrace();  
  66.         }  
  67.     }  
其中在接受到服务端数据之后,对客户端界面处理的方法通过客户端类实现接口MsgListener来完成,这样做能使代码的设计更合理,模块之间调用更加方便。 
Java代码  收藏代码
  1. public interface MsgListener {  
  2.     //在日志上显示消息  
  3.     public void onRecvMsg(String str);  
  4.     //在画图区上画图  
  5.     public void onDraw(int x1,int y1,int x2,int y2,Color color);  
  6.     //显示题目  
  7.     public void onTitle(String str);  
  8.     //清屏  
  9.     public void onclear();  
  10.     //显示评价  
  11.     public void onAccess(String access);  
  12. }  

测试截图: 

  • 查看图片附件
0 0
原创粉丝点击