C#实践炸飞机socket通信
一、前言
- 最近老师要求做课设,实现一个 “炸飞机” 游戏,我是负责UI界面实现和Socket通信实现的,在这里想总结一下我实现Socket的具体过程,对其中的产生的问题和实现的方法进行进行分析。由于我是第一次具体实现Socket通信,所以走了不少弯路,请教了许多人,其中尤其是我的舍友,对我帮助很大。
二、实现思路
我采用的模式是C/S模式(客户端-服务器模式),并且是TCP模式
- 首先是单例化对象,对客户端和服务器都进行了单例化,确保炸飞机时只有一个客户端和一个服务器(因为这个游戏是1V1嘛);
- 然后对客户端的和服务器端 send() 和 receive() 函数进行编写,要注意的一点是:这里不能盲目照搬网络上的代码,其代码使用场景简单,通常是发送一次接收一次(或者是发送一次一直接收),总之对本项目而言是不能适用的;
- 再然后是封装类,封装好之后在其他命名空间中调用接口
三、具体代码
客户类代码
1. 主体代码部分
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace TestBoom
{
class Client //这是封装好的客户端类
{
public String receivestr = null;
private static Client client;
private Socket ClientSocket;
private Client(string ip1, int port1)
{
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Init(ip1, port1);
}
public static Client clientsocket(string ip1,int port1)
{
if (client == null)
{
client = new Client(ip1,port1);
}
return client;
}
private void Init(string ip1, int port1)
{
IPEndPoint iPEnd = new IPEndPoint(IPAddress.Parse(ip1), port1);
ClientSocket.Connect(iPEnd);
Thread reciveThread = new Thread(Recive);
reciveThread.IsBackground = true;
reciveThread.Start();
}
public void Recive()
{
while (true)
{
byte[] Btye = new byte[1024];
ClientSocket.Receive(Btye);
receivestr = Encoding.UTF8.GetString(Btye,0,3);
if (receivestr[0] == '0')
{
Console.WriteLine($"接受对方了轰炸位置{receivestr}");
}
else if(receivestr[0]=='1')
{
Console.WriteLine($"接受轰炸位置结果{receivestr}");
}
}
}
public void Send(int i,int x,int y)
{
string str =Convert.ToString(i)+Convert.ToString(x) + Convert.ToString(y);
byte[] Btye = Encoding.UTF8.GetBytes(str);
ClientSocket.Send(Btye);
if (str[0] == '0')
{
Console.WriteLine($"已发送轰炸位置 {str}");
}
else if (str[0] == '1')
{
Console.WriteLine($"已发送对方轰炸位置结果{str}");
}
}
}
}
2. 具体分析:
1. 首先这个游戏我们必须要知道的一点是我们想要实现两台电脑之间的交互,就必须使用ip和端口进行连接,而想要进行连接就必须使用一个实例化的对象(在这里我没有体现出来,因为实例化对象在另一个from中,在按钮事务响应的函数中进行实例化),而且在这个游戏中,实例化对象必须是单例模型,原因之前提到过,那么实例化对象就必须包含单例化的过程;
public String receivestr = null; //receivestr是接受函数中接收到对方的传输过来的信息,后面用到
private static Client client; //单例化对象所需要的对象
private Socket ClientSocket; //Socket类的一个实例化对象
private Client(string ip1, int port1) //Client()客户端构造函数
{
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Init(ip1, port1);
}
public static Client clientsocket(string ip1,int port1) //单例化实现函数
{
if (client == null) //如果实例化对象不存在,则创建一个
{
client = new Client(ip1,port1);
}
return client; //如果存在,则直接返回存在的那个对象,这样便实现了单例化
}
private void Init(string ip1, int port1) //初始化,用于进行客户端和服务器端的连接
{
IPEndPoint iPEnd = new IPEndPoint(IPAddress.Parse(ip1), port1);
ClientSocket.Connect(iPEnd);
Thread reciveThread = new Thread(Recive);
reciveThread.IsBackground = true;
reciveThread.Start();
}
2. 其次在连接妥当之后,必须进行信息传输,如果而现在假定客户端时先手,则要进行 send() 函数的调用,在函数中你可以发送任意的数据,但必须时btye数组(因为在物理层传输数据是发送的是比特,发送到对方物理层会进行解析还原,但是这些东西C#的Socket类已经封装好了,我们调用接口即可),需要注意的是在使用 send() 的时候必须调用(这个之后再详细说);
public void Send(int i,int x,int y) //这里面的参数 i,x,y 的含义分别是 模式0/1,x坐标, y坐标,可以根据需求改变
{
string str =Convert.ToString(i)+Convert.ToString(x) + Convert.ToString(y); //将数字转化为string类型字符串
byte[] Btye = Encoding.UTF8.GetBytes(str); //将刚刚转化好的string类型字符串转化为byte类型数组
ClientSocket.Send(Btye); //调用Socket类中的Send()函数发送数据
if (str[0] == '0') //判断模式0/1,在己方控制台显示己方发送过去的内容,方便自己查看
{
Console.WriteLine($"已发送轰炸位置 {str}");
}
else if (str[0] == '1')
{
Console.WriteLine($"已发送对方轰炸位置结果{str}");
}
}
3. 最后阐述一下 receive() 函数,再对方(服务器端)接收到你发送的信息之后,一定会返回一个信息(因为下棋是交互的嘛),这时候你便需要一个接收函数 receive() ,这个函数是用来接受对方发送的信息的,但是需要注意的是这个函数会随着你的进程一直运行,在from中是不需要调用的。
public void Recive()
{
while (true) //因为是一直在另一个进程中运行,所以给一个死循环
{
byte[] Btye = new byte[1024]; //接收也是byte数组的
ClientSocket.Receive(Btye);
receivestr = Encoding.UTF8.GetString(Btye,0,3); //转化为string类型
if (receivestr[0] == '0') //判断模式0/1,在己方控制台显示对方发送过来的内容,方便查看对方信息
{
Console.WriteLine($"接受对方了轰炸位置{receivestr}");
}
else if(receivestr[0]=='1')
{
Console.WriteLine($"接受轰炸位置结果{receivestr}");
}
}
}
服务器类代码
1.主体代码部分
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace TestBoom
{
class Server
{
public String receivestr = null;
private Socket SocketWatch;
private Socket SocketSend;
private static Server server = null;
private Server(string ip1, int port1)
{
SocketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Init(ip1,port1);
}
public static Server serversocket(string ip1, int port1)
{
if (server == null)
{
server = new Server(ip1, port1);
}
return server;
}
private void Init(string ip1, int port1)
{
SocketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iPEnd = new IPEndPoint(IPAddress.Parse(ip1), port1);
SocketWatch.Bind(iPEnd);
SocketWatch.Listen(1);
System.Windows.Forms.MessageBox.Show("开始监听...");
Thread thread = new Thread(Listen);
thread.IsBackground = true;
thread.Start();
}
void Listen()
{
while (SocketSend==null)
{
SocketSend = SocketWatch.Accept();
}
System.Windows.Forms.MessageBox.Show("连接成功..." + SocketSend.RemoteEndPoint.ToString());
Thread reciveThread = new Thread(Recive);
reciveThread.IsBackground = true;
reciveThread.Start();
}
public void Recive()
{
while (true)
{
byte[] buffer = new byte[1024];
SocketSend.Receive(buffer);
receivestr = Encoding.UTF8.GetString(buffer, 0, 3);
if (receivestr[0] == '0')
{
Console.WriteLine($"接受对方了轰炸位置{receivestr}");
}
else if (receivestr[0] == '1')
{
Console.WriteLine($"接受我方轰炸位置结果{receivestr}");
}
}
}
public void Send(int i,int x,int y)
{
string str = Convert.ToString(i) + Convert.ToString(x) + Convert.ToString(y);
byte[] buffer = Encoding.UTF8.GetBytes(str);
SocketSend.Send(buffer);
if (str[0] == '0')
{
Console.WriteLine($"已发送轰炸位置 {str}");
}
else if (str[0] == '1')
{
Console.WriteLine($"已发送对方轰炸位置结果{str}");
}
}
}
}
2.具体分析:
- 对于 send() 和 receive() 函数就不过多赘述,主要分析一下 listen() 函数,listen() 函数其实是一个监听函数,只有监听成功之后才能够连接,才可以实例化一个发送Socket对象和一个接收Thread对象,而服务器端也是单例模式的,与客户端结构基本相同。
四、对服务器类和客户类的具体使用
1.代码部分(部分代码,不能直接使用,注释部分即内容)
private void button2_Click(object sender, EventArgs e)
{
if (Plane_Sum < 3)
{
label4.Text = "请先放置坤坤";
}
else
{
if(ipok && portok)
{
label4.Text = "坤坤已放置好";
label3.Text = "你的回合";
client = Client.clientsocket(ip,port); //实例化客户端
serorcli = true;
}
else
{
label4.Text = "没有输入ip或者端口";
}
}
}
private void button1_Click(object sender, EventArgs e)
{
if (Plane_Sum < 3)
{
label4.Text = "请先放置坤坤";
}
else
{
if(ipok && portok)
{
label4.Text = "坤坤已放置好";
label3.Text = "对手回合";
server = Server.serversocket(ip, port); //实例化服务器端
serorcli = false;
}
else
{
label4.Text = "没有输入ip或者端口";
}
}
}
private void button3_Click(object sender, EventArgs e)
{
if (Plane_Sum < 3)
{
label4.Text = "请先放置坤坤";
}
else
{
if (serorcli == false&&ipok&&portok)
{
while (server.receivestr == null) { } //判断有没有接收,直到有接收才可以跳出循环
if (server.receivestr[0] == '0')//接收直接使用,由于接收是处于一直接收的状态
{
PutKunKun2(server.receivestr[1] - '0', server.receivestr[2] - '0');
server.Send(1, Board1[server.receivestr[1] - '0', server.receivestr[2] - '0'], 0); //发送调用
server.receivestr = null; //赋值为null,为下一次接收做准备
}
}
}
}
private void textBox1_ipChanged(object sender, EventArgs e)
{
ip = textBox1.Text; //输入ip
ipok = true;
}
private void textBox2_portChanged(object sender, EventArgs e)
{
int.TryParse(textBox2.Text,out port); //输入port
portok = true;
}
五、问题分析与总结
1.问题分析:
- 在本项目中单例化对象中,对单例化思想并不清楚, 求助于舍友,在他的帮助下明白了,对象没有就创建,有的话就直接返回已经创建的对象。
- 在引用实例化对象的函数时,搞不清楚使用的位置,经过多次试错,多次调整,才明白使用逻辑
- 开始使用 receive() 这一函数时,以为其必须进行调用才行,后来才知道其在对象线程中一直存在,根本不需要调用。
2.总结:
在这个项目中,我对计算机网络中学习的内容有了更深的理解,对Socket通信有了更深的认识,对TCP和UDP也有了不同于书本单薄的理解。
C#实践炸飞机socket通信的更多相关文章
- AgileEAS.NET SOA 中间件平台.Net Socket通信框架-介绍
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- 我看不下去鸟。。。。Java和C#的socket通信真的简单吗?
这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...
- php简单实现socket通信
socket通信的原理在这里就不说了,它的用途还是比较广泛的,我们可以使用socket来做一个API接口出来,也可以使用socket来实现两个程序之间的通信,我们来研究一下在php里面如何实现sock ...
- Socket通信类
package com.imooc; import java.io.BufferedReader; import java.io.IOException; import java.io.InputSt ...
- socket通信
socket通信 一:socket基于Tcp连接,数据传输有保证 二:socket连接的建立过程: 1:服务器监听 2:客户端发出请求 3:建立连接 4:通信 三:一个简单的例子:服务器端每隔一段时间 ...
- Android之Socket通信、List加载更多、Spinner下拉列表
Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务 ...
- .NET开源高性能Socket通信中间件Helios介绍及演示
一:Helios是什么 Helios是一套高性能的Socket通信中间件,使用C#编写.Helios的开发受到Netty的启发,使用非阻塞的事件驱动模型架构来实现高并发高吞吐量.Helios为我们大大 ...
- iOS开发之Socket通信实战--Request请求数据包编码模块
实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncS ...
- AgileEAS.NET SOA 中间件平台.Net Socket通信框架-简单例子-实现简单的服务端客户端消息应答
一.AgileEAS.NET SOA中间件Socket/Tcp框架介绍 在文章AgileEAS.NET SOA 中间件平台Socket/Tcp通信框架介绍一文之中我们对AgileEAS.NET SOA ...
随机推荐
- Java-随机数据生成器(造数据)
概述 简单易用的随机数据生成器.一般用于开发和测试阶段的数据填充.模拟.仿真研究.演示等场景.可以集成到各种类型的java项目中使用. 优点 非常轻量级(不到1M),容易集成,无需过多第三方依赖 简单 ...
- Controller以及RestFul风格
Controller以及RestFul风格 控制器Controller 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方式实现 控制器负责解析用户的请求并将其转换为一个模型 在Spr ...
- KingbaseES 转义字符
在SQL标准中字符串是用单引号括起来的,在KingbaseES中遵守了该标准,如果在字符串中需要使用到单引号,就需要对其进行转义. 方式一:使用E和反斜杠进行转义 方式二:直接用一个单引号来转义 在K ...
- 函数索引引用的函数必须是immutable类型
用户在使用中,可能会用到基于函数的索引,但是函数是非 immutable 类型的,导致函数索引无法创建.如: test=# create index ind_t1 on t1(to_char(crea ...
- js之页面列表加载常用方法总结
导语:最近由于一些事情需要处理,所以没来得及写技术总结了.今天终于可以坐下来好好的梳理一下脉络,说一下那个在日常前端开发过程中,常用到的页面列表加载的方法总结.这里介绍三种方法,分别是分页加载.按钮加 ...
- 分布式文件存储 CephFS的应用场景
块存储 (适合单客户端使用) 典型设备:磁盘阵列,硬盘. 使用场景: a. docker容器.虚拟机远程挂载磁盘存储分配. b. 日志存储. 文件存储 (适合多客户端有目录结构) 典型设备:FTP.N ...
- k8s上安装elasticsearch集群
官方文档地址:https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-quickstart.html yaml文件地址:https://dow ...
- NAT模式下的虚拟机连接主机网络
基于NAT模式的VMware虚拟机(Linux CentOS 7)连接主机(Windows 11)网络 一.什么是NAT模式 虚拟机连接主机网络的三种方式: Bridged(桥接) NAT(网络地址转 ...
- Java复制Word文档
Microsoft Word 提供了许多易于使用的文档操作工具,同时也提供了丰富的功能集供创建复杂的文档使用.在使用的时候,你可能需要复制一个文档里面的内容到另一个文档.本文介绍使用Spire.Doc ...
- 自然语言处理NLP程序包(NLTK/spaCy)使用总结
NLTK和SpaCy是NLP的Python应用,提供了一些现成的处理工具和数据接口.下面介绍它们的一些常用功能和特性,便于对NLP研究的组成形式有一个基本的了解. NLTK Natural Langu ...