socket通信

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

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

[color=green]接下来用代码进行说明:[/color]
1、服务端:首先建立一个ServerSocket服务器端口号,当有客户端访问时,交给服务端线程处理。[/size]
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);

}

[size=medium]服务端线程:得到连接入socket的一个输入输出流,Output写数据送到客户机,Input读取数据到服务器,自定义通信协议,根据接受数据包的不同类型进行不同处理。线程类中的run方法以及部分想客户端发送消息的方法如下,更多的向客户端发送不同类型数据的方法不再赘述:[/size]
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();
}
}

[size=medium] 2、客户端:创建一个客户端窗体,并初始化界面,然后启动客户端线程处理。这部分比较简单不贴代码。
客户端线程:创建对应服务端的socket套接字,连接服务器,根据通信协议,依不同的数据包类型进行处理,run方法如下[/size]
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();
}
}
[size=medium]其中在接受到服务端数据之后,对客户端界面处理的方法通过客户端类实现接口MsgListener来完成,这样做能使代码的设计更合理,模块之间调用更加方便。[/size]
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);
}

[size=medium]测试截图:[/size]
[img]http://dl2.iteye.com/upload/attachment/0103/1941/95a6e45e-a5f0-3b4d-96ec-62b96cfe0511.jpg[/img]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值