正文
许多计算机网络课程需要进行编写socket聊天室的课程实验,本博客提供了一个用c#编写的简易解决方案,没有图形界面。如果你的电脑上安装过.net,应该可以在一分钟之内把程序跑起来。
(代码链接在文末)
什么是Socket
简单来说Socket就是一组编程接口,方便程序员在程序中创建远程连接。本质上它是对TCP/UDP协议的封装,方便程序员编写相关内容。
使用方法
只需加入代码using System.Net.Sockets;
就可以使用C#中的Socket功能了。Socket通讯的建立可以用下图简单的描述出来:
所以,编写的方法就很清晰了:首先客户端和服务端都需要新建Socket对象,用构造函数socket()去进行初始化;然后服务端需要用Socket的bind()和listen()方法监听对应端口;服务端调用accept()方法,客户端调用connect()方法,两个Socket会进行连接,随后便可以进行一系列的数据交换直至close()关闭。
代码
由于没有图形界面,代码非常简单,总共只有三个类,分别是客户端、服务端以及程序入口。我这里展示出比较重要的方法:
服务端
初始化
public Service()
{
// 初始化socket
socketSevice = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 初始化用户列表
userList = new List<Socket>();
}
可以自己去c#的api官网查一下这些参数的含义
开始监听
public void Run()
{
// 打印IP
string AddressIP = string.Empty;
foreach (IPAddress _IPAddress in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
{
if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
{
AddressIP = _IPAddress.ToString();
}
}
Console.WriteLine(AddressIP);
// 监听指定端口
// 这里使用IPAddress.Any是比较正确的写法,如果直接写127.0.0.1可能会导致不同设备连接出现问题
socketSevice.Bind(new IPEndPoint(IPAddress.Any,7788));
socketSevice.Listen(10);
Console.WriteLine("服务器已启动");
// 异步调用接收用户的线程
Thread accThread = new Thread(Accept);
accThread.IsBackground = true;
accThread.Start();
Console.ReadLine();
}
其实这一步骤的关键代码只有两行,也就是Bind和Listen那两行。
如果你这里写的不是IPAddress.Any
,在你用两台主机测试这个程序的时候可能会遇到无数问题,不过如果你只想在一台电脑上跑,写成127.0.0.1是没有问题的。
这个方法里的最后几行是启动了一条专门等待接收用户连接的线程。关于.net的多线程写法,有兴趣的话可以持续关注我,我后面会更新相关内容的。
连接用户
// 接受用户线程
private void Accept()
{
//接受连接
Socket clientSocket = socketSevice.Accept();
userList.Add(clientSocket);
//打印对应的IP地址
Console.WriteLine(IPToAddress(clientSocket)+"已接入聊天室");
// 异步调用对应用户的接收信息线程
Thread RecvThread = new Thread(ReceMessage);
RecvThread.IsBackground = true;
RecvThread.Start(clientSocket);
// 递归调用,保证接受用户的线程一直在运行
Accept();
}
在有用户发起连接申请之前,该线程会一直阻塞在第一行,直到连接上用户之后就会打印相关信息,激活对应这名用户的信息接收线程,并且重新启动一条本线程。
发送和接收
使用Recive()和Send()方法
客户端
连接服务端
public void Connected(string Ip,int port)
{
clientSocket.Connect(Ip,port);
Console.WriteLine("连接成功");
// 异步调用接收信息的线程
Thread RecvThread = new Thread(RecvMessage);
RecvThread.IsBackground = true;
RecvThread.Start();
}
非常简单。这里的IP,如果是运行在同一台电脑上的,就传入127.0.0.1就好了,如果是在两台主机上分别运行客户端和服务端,那么你需要保证这两台主机在同一局域网下,或者你把服务端运行在一台真正的服务器上。
校园网可能是不管用的,实在不行可以试一下手机的热点。
如果你想要尝试在两台主机上运行,可能遇到的问题有很多,在这里我列出几项可能的原因和解决方法:
- 前文中提到的要写IPAddress.Any
- 两台主机要处于同一局域网下。校园网可能是不行的,可以试着用手机热点。
- 服务端电脑的防火墙设置问题(可能会阻止外来访问),尤其主要这点如果你的客户端终端报错说:目标计算机积极拒绝……
- 端口被占用了(这个可能性是比较小的)
程序入口
public static void Main(String[] args) {
Console.WriteLine("输入0以启动服务端,输入1以启动客户端");
while(true) {
string a=Console.ReadLine();
int b=int.Parse(a);
if(b==0) {
Service service = new Service();
service.Run();
} else if(b==1) {
Client client = new Client();
client.Run();
} else {
Console.WriteLine("输入错误,请重新输入");
}
}
}
朴实无华的Main函数。这样做主要是为了方便测试,你可以直接在vsc之类的软件里面多开几个终端,然后分别运行客户端和服务端。
总结
以下贴上github链接,可以直接取用代码。
iLoner121/SocketChatRoom: 湖南大学计算机网络实验三:基于socket的多人聊天室程序 (github.com)
通过本篇博客的介绍,你应该能顺利通过学校的计算机网络课程实验(你们的计网课总不会强制要求图形界面吧?)
如果要图形界面也没关系,我打算过一段时间做几篇相关.net的图形界面制作方法,比如如何制作简单的windows窗体程序或者web程序。vs内置了丰富的框架和模板,支持用户快速的制作出一个有图形界面的程序。