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服务器端口号,当有客户端访问时,交给服务端线程处理。
服务端线程:得到连接入socket的一个输入输出流,Output写数据送到客户机,Input读取数据到服务器,自定义通信协议,根据接受数据包的不同类型进行不同处理。线程类中的run方法以及部分想客户端发送消息的方法如下,更多的向客户端发送不同类型数据的方法不再赘述:
2、客户端:创建一个客户端窗体,并初始化界面,然后启动客户端线程处理。这部分比较简单不贴代码。
客户端线程:创建对应服务端的socket套接字,连接服务器,根据通信协议,依不同的数据包类型进行处理,run方法如下
测试截图:
本文主要叙述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); }
测试截图:
上一篇: ApacheReferenceManual9_PHP教程
下一篇: 循环不知道原因结束
推荐阅读
-
UDP通信 博客分类: java javaudpsocket
-
socket通信 博客分类: java javasockettcp
-
socket通信 博客分类: java javasockettcp
-
UDP通信 博客分类: java javaudpsocket
-
收货地址之三级联动式菜单(省、市、区),利用AJAX通信,面向对象编程,并且将菜单放入指定容器中
-
FTP 协议讲座 博客分类: 协议FTP socket防火墙
-
socket示例
-
java : mozilla rhino js Test URL or Socket 博客分类: javajavascript javarhinojstest
-
在websocket实现zlib压缩通信(Z_SYNC_FLUSH模式的应用)
-
用PHP的socket实现客户端到服务端的通信实例详解