最近在做个多人游戏的demo,试试自己编写服务器端,在写之前,就不介绍Tcp和Udp的区别了,网上资料很多,在看socket编程之前最好先去看看基础的Tcp和Udp的理论内容,再去看Socket编程会好很多。
C#的socket编程主要用到一下命名空间:
- System.Net;//socket的重要基础
- System.Net.Sockets;//socket的重要基础
- System.Text;//因为socket只能发送byte数组,也只能接收byte数组,需要把接收到的byte数组转化为字符串
- System.Threading;//主要用来开启一个负责监听和接收消息的线程
socket的常用类和方法
相关类:
- IPAddress:包含了一个IP地址
- IPEndPoint:包含了一对IP地址和端口号
相关方法
- Socket():创建一个Socket
- Bind():绑定一个本地的IP和端口号(IPEndPoint)
- Listen():让Socket侦听传入的连接吃那个病,并指定侦听队列容量
- Connect():初始化与另一个Socket的连接
- Accept():接收连接并返回一个新的Socket
- Send():输出数据到Socket,只能发送一个byte数组
- Receive():从Socket中读取数据,返回接收到的数据的长度
- Close():关闭Socket,销毁连接
客户端和服务器端结构如图
通过上图,我们可以看出,首先服务器会创建一个负责监听的socket,然后客户端通过socket连接到服务器指定端口,最后服务器端负责监听的socket,监听到客户端有连接过来了,就创建一个负责和客户端通信的socket。
下面我们来看下Socket更具体的通信过程:
Socket的通讯过程
服务器端:
01,申请一个socket
02,绑定到一个IP地址和一个端口上
03,开启侦听,等待接收连接
客户端:
01,申请一个socket
02,连接服务器(指明IP地址和端口号)
服务器端接收到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通信,原监听socket继续监听。
注意:负责通信的Socket不能无限创建,创建的数量和操作系统有关。
Socket的构造函数
Public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolTYpe)
AddressFamily:指定Socket用来解析地址的寻址方案。例如:InterNetWork指示当Socket使用一个IP版本4地址连接
SocketType:定义要打开的Socket的类型
Socket类使用ProtocolType枚举向Windows Sockets API通知所请求的协议
示例
IPAddress addr = IPAddress.Parse("127.0.0.1");
/*
注意如果addr是指定ip地址,那么就只能在同一个ip下进行通信,如果作为服务器端的话addr必须是IpAddress.any,
表示任意ip都可以在指定端口下访问服务器
*/
IPEndPoint endp = new IPEndPoint(addr,,9000);
服务端先绑定:serverWelcomeSocket.Bind(endp)
客户端再连接:clientSocket.Connect(endp)
注意
1,一个Socket一次只能连接一台主机
2,Socket关闭后无法再次使用
3,每个Socket对象只能与一台远程主机连接。如果你想连接到多台远程主机,你必须创建多个Socket对象。
服务器端代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Server {
class Program {
//记录通信用的scoket的字典
static Dictionary sockets = new Dictionary ();
static void Main (string[] args) {
//设置ip和端口
IPAddress ipAddress = IPAddress.Any;
int port = 30001;
//绑定本机的IP和端口
IPEndPoint endPoint = new IPEndPoint (ipAddress, port);
//新建socket
Socket socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定服务器的监听Socket
try {
socket.Bind (endPoint);
//设置最大连接数
socket.Listen (10); //如果是0表示不限制连接数
ShowMessage ("开始监听");
// 后台线程用于监听来连接上来的客户端
Thread t = new Thread (AcceptInfo);
t.IsBackground = true;
t.Start (socket);
} catch (Exception e) {
// 打印错误信息
ShowMessage (e.Message);
}
Console.ReadKey ();
}
//打印信息
static void ShowMessage (string msg) {
Console.WriteLine (msg);
}
//创建链接成功后用于通信的Socket
static void AcceptInfo (Object o) {
Socket socket = o as Socket;
while (true) {
try {
//通信用的socket,根据连接过来的scoket返回一个新的用于通信的socket
Socket tSocket = socket.Accept ();
string point = tSocket.RemoteEndPoint.ToString ();
//把通信socket加入字典
sockets.Add (point, tSocket);
ShowMessage (point + " 连接成功");
//接收客户端的信息
Thread t = new Thread (ReceiveMsg);
t.IsBackground = true;
t.Start (tSocket);
} catch (Exception e) {
ShowMessage (e.Message);
break;
}
}
}
static void ReceiveMsg (Object o) {
Socket client = o as Socket;
while (true) {
try {
byte[] buffer = new byte[1024 * 1024];
// 获得实际接受的数据的长度
int n = client.Receive (buffer);
// 如果是一个有内容的请求
if (n > 0) {
GetRequestByCode (buffer[0], buffer, n);
}
// string words = Encoding.UTF8.GetString(buffer,0,n);
// ShowMessage(client.RemoteEndPoint.ToString() + ":" + words);
} catch (Exception e) {
ShowMessage (e.Message);
break;
}
}
}
///
/// 根据请求的code来获得对应的请求,即buffer[0] = code
///
/// 请求的code
/// 请求的内容
/// 实际请求内容的长度
static void GetRequestByCode (byte code, byte[] buffer, int length) {
switch (code) {
case 0:
GetRequestContent (buffer, length);
break;
case 1:
GetRequestContent (buffer, length);
break;
default:
break;
}
}
///
/// 负责获取请求的内容
///
///
///
static void GetRequestContent (byte[] buffer, int length) {
//因为指定了buffer[0]是请求的code区分是哪个请求,请求内容从1开始
string words = Encoding.UTF8.GetString (buffer, 1, length);
Console.WriteLine (words);
}
}
}
客户端代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace Client {
class Program {
static Socket client = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main (string[] args) {
IPAddress ip = IPAddress.Parse ("39.107.234.74");
IPEndPoint point = new IPEndPoint (ip, 30001);
//开始连接到服务器
try {
client.Connect (point);
ShowMessage ("服务器" + client.RemoteEndPoint.ToString ());
ShowMessage ("客户端" + client.LocalEndPoint.ToString ());
SendMessage (client, 0, "我是" + client.LocalEndPoint.ToString () + "请求0过来了");
// SendMessage(client,1,"我是" + client.LocalEndPoint.ToString() + "请求1过来了");
} catch (Exception e) {
ShowMessage (e.Message);
}
Console.ReadKey ();
}
static void ShowMessage (string msg) {
Console.WriteLine (msg);
}
///
/// 发送请求(code存在byte[]的0,content存在byte[]0后面的空间里)
///
/// 发送请求的socket
/// 请求的编码
/// 请求的内容
static void SendMessage (Socket client, byte requestCode, string context) {
if (client != null && client.Connected) {
try {
byte[] buffer = Encoding.UTF8.GetBytes (context);
//新的请求格式buffer[0]是请求的编码,buffe后面的内容是请求的内容
List list = new List ();
list.Add (requestCode);
list.AddRange (buffer);
byte[] newbuffer = list.ToArray ();
client.Send (newbuffer);
} catch (Exception e) {
ShowMessage (e.Message);
}
}
}
}
}
至此结束。