当前位置:   article > 正文

计网实验(三):基本Winsock编程_了解winsock编程原理; 2、熟悉windows网络编程接口;流程图

了解winsock编程原理; 2、熟悉windows网络编程接口;流程图

git地址:https://github.com/MyUsernameIsJX/WinSock

目录

1. 实验目的及环境

2. 实验要求

3. 实验内容及结果

4. 实验中遇到的问题以及解决方法

5. 实验总结


1. 实验目的及环境

1、了解Winsock编程原理;

2、熟悉Windows网络编程接口;

Visual C或 C、VB等。

2. 实验要求

编写一个C/S通讯程序。

具体要求:

  1. 使用WINSOCK 通讯:WINSOCKWindows Sockets API的简称,已经成为Windows广泛应用的、开放的、支持多种协议、事实上的网络编程接口标准。
  2. 客户方程序与服务方程序位于两台不同的机器上,在客户方通过指定服务方的IP地址和端口号来通讯;
  3. 服务器程序,始终处于监听状态,具有连续接收客户发送的信息的能力(发送的信息任意)。
  4. 至少要保证2个客户端之间可以相互通信,注意界面设计的合理性,操作的友好性

3. 实验内容及结果

实验选择的是C#脚本语言

界面设计:

Server:

Client:

代码部分:

服务器代码

全局变量定义:

  1. //服务器端的socket
  2. private Socket serverSocket = null;
  3. //服务器端线程
  4. private Thread serverThread = null;
  5. //保存已连接的客户端socket
  6. private List<Socket> clientSocketList = null;
  7. //保存已开启的接收数据的客户端线程
  8. private List<Thread> clientThreadList = null;
  9. // 接受数据缓冲区
  10. private byte[] result = new byte[1024];
  11. // 跨线程更新UI
  12. Action<String> AsyncUIDelegate;
  13. // 服务器IP
  14. private string IP = "";
  15. // 服务器开放的端口
  16. private int Port;
  17. // 最大用户连接数
  18. private int MAXCLIENTNUM = 5;
  19. // 当前用户连接数
  20. private int iClientNum = 0;

初始化控件:

  1. // 将本机所有可用ip加入下拉框内
  2. String[] ipAddress = getLocalAddress();
  3. for(int i = 0;i<ipAddress.Length;i++)
  4. {
  5. if(ipAddress[i].IndexOf(".") == 3)
  6. {
  7. cboIP.Items.Add(ipAddress[i]);
  8. }
  9. }
  10. // 初始化文本显示
  11. tbMonitor.ReadOnly = true;
  12. tbSend.ReadOnly = false;
  13. // 初始化按钮状态
  14. btnStart.Enabled = true;
  15. btnStop.Enabled = false;
  16. // 初始化ip和port
  17. cboIP.SelectedIndex = 1;
  18. tbPort.Text = "8888";
  19. // 子线程更新UI
  20. AsyncUIDelegate = delegate (string message)
  21. {
  22. addMonitorMessage(message);
  23. };
  24. addMonitorMessage("聊天系统初始化完成.");

addMonitorMessage():

  1. //向聊天框添加信息
  2. private void addMonitorMessage(String message)
  3. {
  4. if("".Equals(tbMonitor.Text))
  5. {
  6. tbMonitor.Text = message + "\r\n";
  7. }
  8. else
  9. {
  10. tbMonitor.Text += message + "\r\n";
  11. }
  12. return;
  13. }

getLocakAddress():

  1. // 获得本机所有ip地址
  2. private String[] getLocalAddress()
  3. {
  4. // 获得主机名
  5. String hostName = Dns.GetHostName();
  6. // 根据主机名查找ip
  7. IPHostEntry iPHostEntry = Dns.GetHostEntry(hostName);
  8. String[] result = new String[iPHostEntry.AddressList.Length];
  9. int i = 0;
  10. foreach (IPAddress iPAddress in iPHostEntry.AddressList)
  11. {
  12. result[i] = iPHostEntry.AddressList[i].ToString();
  13. i++;
  14. }
  15. return result;
  16. }

创建服务器:

  1. // 启动服务器
  2. private void btnStart_Click(object sender, EventArgs e)
  3. {
  4. addMonitorMessage("正在开启服务器......");
  5. startServer();
  6. btnStart.Enabled = false;
  7. btnStop.Enabled = true;
  8. }
  9. private void startServer()
  10. {
  11. // 初始化客户端socket管理列表
  12. clientSocketList = new List<Socket>();
  13. // 初始化客户端线程管理列表
  14. clientThreadList = new List<Thread>();
  15. IP = cboIP.SelectedItem.ToString();
  16. Port = Int32.Parse(tbPort.Text.ToString());
  17. IPAddress iPAddress = IPAddress.Parse(IP);
  18. serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
  19. IPEndPoint pEndPoint = new IPEndPoint(iPAddress, Port);
  20. serverSocket.Bind(pEndPoint);
  21. serverSocket.Listen(10);
  22. serverThread = new Thread(LitenerClientConnect);
  23. serverThread.Start();
  24. tbMonitor.Invoke(AsyncUIDelegate, "服务器启动成功!");
  25. }

监听客户端的连接:

  1. // 监听客户端的链接
  2. private void LitenerClientConnect()
  3. {
  4. while(true)
  5. {
  6. Socket clientSocket = serverSocket.Accept();
  7. // 将接受到的客户端socket添加到clientSocketList中
  8. clientSocketList.Add(clientSocket);
  9. // 获取客户端的IP和端口号
  10. IPEndPoint clientIpEndPoint = clientSocket.RemoteEndPoint as IPEndPoint;
  11. IPAddress clientIP = clientIpEndPoint.Address;
  12. int clientPort = clientIpEndPoint.Port;
  13. tbMonitor.Invoke(AsyncUIDelegate, "IP:" + clientIP.ToString()+"\t
  14. 端口:" + clientPort.ToString() +"\t已成功连接到服务器.");
  15. // 向客户端发送信息
  16. // 使用UTF8编码
  17. clientSocket.Send(Encoding.UTF8.GetBytes("\t连接成功!\t本机IP:" +
  18. clientIP.ToString() + "\t\t端口:" + clientPort.ToString()));
  19. Thread receiverMessage = new Thread(receiverData);
  20. clientThreadList.Add(receiverMessage);
  21. receiverMessage.Start(clientSocket);
  22. iClientNum++;
  23. }
  24. }

接受客户端的消息:

  1. // 接受客户端的消息
  2. private void receiverData(Object clientSocket)
  3. {
  4. Socket myClientSocket = (Socket)clientSocket;
  5. while(true)
  6. {
  7. try // 防止服务器端接受消息堵塞
  8. {
  9. int receiverNum = myClientSocket.Receive(result);
  10. string message = Encoding.ASCII.GetString(result, 0, receiverNum);
  11. // 判断收到EXIT,如果是空则是客户端断开连接
  12. if ("EXIT".Equals(message))
  13. {
  14. clientSocketList.Remove(myClientSocket);
  15. iClientNum--;
  16. IPEndPoint clientIpEndPoint =
  17. myClientSocket.RemoteEndPoint as IPEndPoint;
  18. IPAddress clientIP = clientIpEndPoint.Address;
  19. int clientPort = clientIpEndPoint.Port;
  20. tbMonitor.Invoke(AsyncUIDelegate, "IP:" + clientIP.ToString() +
  21. "\t端口:" + clientPort.ToString() +"\t已断开连接.");
  22. break;
  23. }
  24. else
  25. {
  26. for (int i = 0; i < clientSocketList.Count; i++)
  27. {
  28. clientSocketList[i].Send(Encoding.UTF8.GetBytes(message));
  29. }
  30. tbMonitor.Invoke(AsyncUIDelegate, message);
  31. }
  32. }
  33. catch(Exception)
  34. {
  35. myClientSocket.Close();
  36. break;
  37. }
  38. }
  39. }

关闭服务器

  1. private void btnStop_Click(object sender, EventArgs e)
  2. {
  3. addMonitorMessage("正在关闭服务器......");
  4. stopServer();
  5. btnStart.Enabled = true;
  6. btnStop.Enabled = false;
  7. }
  8. // 关闭服务器
  9. private void stopServer()
  10. {
  11. serverSocket.Close();
  12. serverThread.Abort();
  13. for (int i = 0; i < clientSocketList.Count; i++)
  14. {
  15. // 先检查连接到的客户端是否仍在连接,然后断开正在连接的客户端
  16. if(clientSocketList[i].Connected == true)
  17. {
  18. clientSocketList[i].Disconnect(true);
  19. }
  20. clientSocketList.Remove(clientSocketList[i]);
  21. }
  22. // 关闭所有客户端线程
  23. for (int i = 0; i < clientThreadList.Count; i++)
  24. {
  25. clientThreadList[i].Abort();
  26. }
  27. tbMonitor.Invoke(AsyncUIDelegate, "服务器关闭!");
  28. btnStart.Enabled = true;
  29. btnStop.Enabled = false;
  30. }

获取GUI中的IPPort

  1. private void tbPort_TextChanged(object sender, EventArgs e)
  2. {
  3. Port = Int32.Parse(tbPort.Text.ToString());
  4. }
  5. private void cboIP_SelectedIndexChanged(object sender, EventArgs e)
  6. {
  7. IP = cboIP.SelectedItem.ToString();
  8. }

添加新的客户端form

  1. private void btnAddClient_Click(object sender, EventArgs e)
  2. {
  3. if (iClientNum == MAXCLIENTNUM)
  4. {
  5. MessageBox.Show("用户数已达最大值,无法创建新的客户机!");
  6. return;
  7. }
  8. Form clientForm = new Form2();
  9. clientForm.Show();
  10. }

客户端代码

所有全局变量以及含义:

  1. // 客户端接收服务器消息线程
  2. private Thread threadClient = null;
  3. // 服务器socket
  4. private Socket socketClient = null;
  5. // 客户名字
  6. private String clientName = "";
  7. // 客户端端口
  8. private int clientPort;
  9. // 客户端IP
  10. private IPAddress clientIP = null;
  11. // 客户端IPEndPoint
  12. private IPEndPoint clientIpEndPoint = null;
  13. // 消息缓存区
  14. byte[] recvBuffer = null;

初始化控件:

  1. private void initView()
  2. {
  3. btnConnectToServer.Enabled = true;
  4. btnExit.Enabled = false;
  5. tbMonitor.ReadOnly = true;
  6. tbIP.ForeColor = Color.Red;
  7. tbPort.ForeColor = Color.Red;
  8. tbName.ForeColor = Color.Red;
  9. }

连接服务器:

  1. // 连接服务器
  2. private void btnConnectToServer_Click(object sender, EventArgs e)
  3. {
  4. try // 防止输入的信息出现错误 eg:port中含有字母
  5. {
  6. clientIP = IPAddress.Parse(tbIP.Text.ToString().Trim());
  7. clientPort = Int32.Parse(tbPort.Text.ToString().Trim());
  8. clientName = tbName.Text.ToString().Trim();
  9. }
  10. catch(Exception)
  11. {
  12. MessageBox.Show("输入信息不合法,请重新输入!");
  13. return;
  14. }
  15. btnConnectToServer.Enabled = false;
  16. btnExit.Enabled = true;
  17. clientIpEndPoint = new IPEndPoint(clientIP, clientPort);
  18. socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  19. tbIP.ReadOnly = true;
  20. tbPort.ReadOnly = true;
  21. tbName.ReadOnly = true;
  22. try
  23. {
  24. socketClient.Connect(clientIpEndPoint);
  25. }
  26. catch
  27. {
  28. MessageBox.Show("连接失败\r\n");
  29. btnConnectToServer.Enabled = true;
  30. btnExit.Enabled = false;
  31. return;
  32. }
  33. threadClient = new Thread(recvMsg);
  34. threadClient.Start();
  35. }

接受服务器发来的消息:

  1. // 接受服务器端发来的消息
  2. private void recvMsg()
  3. {
  4. while(true)
  5. {
  6. try //
  7. {
  8. // 临时存储接收到的信息 1kb
  9. recvBuffer = new byte[1024];
  10. //将客户端套接字接收到的数据存入内存缓冲区,并获取长度
  11. int length = socketClient.Receive(recvBuffer);
  12. string strRecvMsg = Encoding.UTF8.GetString(recvBuffer, 0, length);
  13. addMonitorMessage(strRecvMsg);
  14. }
  15. catch
  16. {
  17. addMonitorMessage("远程服务器已经中断连接\r\n");
  18. btnConnectToServer.Enabled = true;
  19. break;
  20. }
  21. }
  22. }

客户端发送消息:

  1. private void btnCSend_Click(object sender, EventArgs e)
  2. {
  3. try
  4. {
  5. string strMsg = tbSendMsg.Text.ToString().Trim();
  6. if("".Equals(strMsg))
  7. {
  8. MessageBox.Show("消息不能为空!");
  9. return;
  10. }
  11. if(btnConnectToServer.Enabled == true)
  12. {
  13. MessageBox.Show("请先连接到服务器,再发送消息");
  14. return;
  15. }
  16. tbSendMsg.Text = "";
  17. strMsg = "[" + clientName + "]:" + strMsg;
  18. socketClient.Send(Encoding.UTF8.GetBytes(strMsg));
  19. }
  20. catch
  21. {
  22. MessageBox.Show("消息发送失败,请检查是否连接到服务器");
  23. return;
  24. }
  25. }

断开连接:

  1. private void btnExit_Click(object sender, EventArgs e)
  2. {
  3. try
  4. {
  5. // 通知服务器即将断开连接
  6. socketClient.Send(Encoding.UTF8.GetBytes("EXIT"));
  7. socketClient.Dispose();
  8. socketClient.Close();
  9. btnExit.Enabled = false;
  10. btnConnectToServer.Enabled = true;
  11. tbName.ReadOnly = false;
  12. }
  13. catch
  14. {
  15. MessageBox.Show("客户机关闭失败!");
  16. return;
  17. }
  18. }

     实验结果

服务器的初始化和启动:

添加客户以及连接服务器:

服务器发送消息:

客户端发送消息:

关闭客户端:

关闭服务器:

4. 实验中遇到的问题以及解决方法

问题:

多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,访问窗口的控件时无法对其控制。

解决方法:

将窗口构造函数中的CheckForIllegalCrossThreadCalls设置为false;然后就能安全的访问窗体控件。

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;

问题:

服务器创建时输入的ip可能不是本机的ip

解决方法:

       将ip输入框改为下拉框,内容是本机所有可用ipv4的ip

问题:

传送消息之后,收到的中文显示乱码

解决方法:

       发送消息时使用UTF8编码

clientSocketList[i].Send(Encoding.UTF8.GetBytes(message));

5. 实验总结

在调试的过程中,巩固提高了针对问题、错误的分析能力和逻辑思维能力。

学会了网络编程的一些基本知识。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/147068
推荐阅读
相关标签
  

闽ICP备14008679号