赞
踩
git地址:https://github.com/MyUsernameIsJX/WinSock
目录
1、了解Winsock编程原理;
2、熟悉Windows网络编程接口;
Visual C或 C、VB等。
编写一个C/S通讯程序。
具体要求:
实验选择的是C#脚本语言
界面设计:
Server:
Client:
代码部分:
服务器代码
全局变量定义:
- //服务器端的socket
- private Socket serverSocket = null;
- //服务器端线程
- private Thread serverThread = null;
- //保存已连接的客户端socket
- private List<Socket> clientSocketList = null;
- //保存已开启的接收数据的客户端线程
- private List<Thread> clientThreadList = null;
- // 接受数据缓冲区
- private byte[] result = new byte[1024];
- // 跨线程更新UI
- Action<String> AsyncUIDelegate;
- // 服务器IP
- private string IP = "";
- // 服务器开放的端口
- private int Port;
- // 最大用户连接数
- private int MAXCLIENTNUM = 5;
- // 当前用户连接数
- private int iClientNum = 0;
初始化控件:
- // 将本机所有可用ip加入下拉框内
- String[] ipAddress = getLocalAddress();
- for(int i = 0;i<ipAddress.Length;i++)
- {
- if(ipAddress[i].IndexOf(".") == 3)
- {
- cboIP.Items.Add(ipAddress[i]);
- }
-
- }
- // 初始化文本显示
- tbMonitor.ReadOnly = true;
- tbSend.ReadOnly = false;
-
- // 初始化按钮状态
- btnStart.Enabled = true;
- btnStop.Enabled = false;
-
- // 初始化ip和port
- cboIP.SelectedIndex = 1;
- tbPort.Text = "8888";
-
- // 子线程更新UI
- AsyncUIDelegate = delegate (string message)
- {
- addMonitorMessage(message);
- };
- addMonitorMessage("聊天系统初始化完成.");
addMonitorMessage():
- //向聊天框添加信息
- private void addMonitorMessage(String message)
- {
- if("".Equals(tbMonitor.Text))
- {
- tbMonitor.Text = message + "\r\n";
- }
- else
- {
- tbMonitor.Text += message + "\r\n";
- }
- return;
- }
getLocakAddress():
- // 获得本机所有ip地址
- private String[] getLocalAddress()
- {
- // 获得主机名
- String hostName = Dns.GetHostName();
-
- // 根据主机名查找ip
- IPHostEntry iPHostEntry = Dns.GetHostEntry(hostName);
- String[] result = new String[iPHostEntry.AddressList.Length];
- int i = 0;
- foreach (IPAddress iPAddress in iPHostEntry.AddressList)
- {
- result[i] = iPHostEntry.AddressList[i].ToString();
- i++;
- }
- return result;
-
- }
创建服务器:
- // 启动服务器
- private void btnStart_Click(object sender, EventArgs e)
- {
- addMonitorMessage("正在开启服务器......");
- startServer();
- btnStart.Enabled = false;
- btnStop.Enabled = true;
- }
- private void startServer()
- {
- // 初始化客户端socket管理列表
- clientSocketList = new List<Socket>();
- // 初始化客户端线程管理列表
- clientThreadList = new List<Thread>();
- IP = cboIP.SelectedItem.ToString();
- Port = Int32.Parse(tbPort.Text.ToString());
- IPAddress iPAddress = IPAddress.Parse(IP);
- serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
- IPEndPoint pEndPoint = new IPEndPoint(iPAddress, Port);
- serverSocket.Bind(pEndPoint);
- serverSocket.Listen(10);
- serverThread = new Thread(LitenerClientConnect);
- serverThread.Start();
- tbMonitor.Invoke(AsyncUIDelegate, "服务器启动成功!");
- }
监听客户端的连接:
- // 监听客户端的链接
- private void LitenerClientConnect()
- {
- while(true)
- {
- Socket clientSocket = serverSocket.Accept();
- // 将接受到的客户端socket添加到clientSocketList中
- clientSocketList.Add(clientSocket);
- // 获取客户端的IP和端口号
- IPEndPoint clientIpEndPoint = clientSocket.RemoteEndPoint as IPEndPoint;
- IPAddress clientIP = clientIpEndPoint.Address;
- int clientPort = clientIpEndPoint.Port;
- tbMonitor.Invoke(AsyncUIDelegate, "IP:" + clientIP.ToString()+"\t
- 端口:" + clientPort.ToString() +"\t已成功连接到服务器.");
- // 向客户端发送信息
- // 使用UTF8编码
- clientSocket.Send(Encoding.UTF8.GetBytes("\t连接成功!\t本机IP:" +
- clientIP.ToString() + "\t\t端口:" + clientPort.ToString()));
- Thread receiverMessage = new Thread(receiverData);
- clientThreadList.Add(receiverMessage);
- receiverMessage.Start(clientSocket);
- iClientNum++;
- }
- }
接受客户端的消息:
- // 接受客户端的消息
- private void receiverData(Object clientSocket)
- {
- Socket myClientSocket = (Socket)clientSocket;
- while(true)
- {
- try // 防止服务器端接受消息堵塞
- {
- int receiverNum = myClientSocket.Receive(result);
- string message = Encoding.ASCII.GetString(result, 0, receiverNum);
- // 判断收到EXIT,如果是空则是客户端断开连接
- if ("EXIT".Equals(message))
- {
- clientSocketList.Remove(myClientSocket);
- iClientNum--;
- IPEndPoint clientIpEndPoint =
- myClientSocket.RemoteEndPoint as IPEndPoint;
- IPAddress clientIP = clientIpEndPoint.Address;
- int clientPort = clientIpEndPoint.Port;
- tbMonitor.Invoke(AsyncUIDelegate, "IP:" + clientIP.ToString() +
- "\t端口:" + clientPort.ToString() +"\t已断开连接.");
- break;
- }
- else
- {
- for (int i = 0; i < clientSocketList.Count; i++)
- {
- clientSocketList[i].Send(Encoding.UTF8.GetBytes(message));
- }
- tbMonitor.Invoke(AsyncUIDelegate, message);
- }
- }
- catch(Exception)
- {
- myClientSocket.Close();
- break;
- }
- }
- }
关闭服务器
- private void btnStop_Click(object sender, EventArgs e)
- {
- addMonitorMessage("正在关闭服务器......");
- stopServer();
- btnStart.Enabled = true;
- btnStop.Enabled = false;
- }
- // 关闭服务器
- private void stopServer()
- {
- serverSocket.Close();
- serverThread.Abort();
- for (int i = 0; i < clientSocketList.Count; i++)
- {
- // 先检查连接到的客户端是否仍在连接,然后断开正在连接的客户端
- if(clientSocketList[i].Connected == true)
- {
- clientSocketList[i].Disconnect(true);
- }
- clientSocketList.Remove(clientSocketList[i]);
- }
- // 关闭所有客户端线程
- for (int i = 0; i < clientThreadList.Count; i++)
- {
- clientThreadList[i].Abort();
- }
- tbMonitor.Invoke(AsyncUIDelegate, "服务器关闭!");
- btnStart.Enabled = true;
- btnStop.Enabled = false;
- }
获取GUI中的IP和Port
- private void tbPort_TextChanged(object sender, EventArgs e)
- {
- Port = Int32.Parse(tbPort.Text.ToString());
- }
- private void cboIP_SelectedIndexChanged(object sender, EventArgs e)
- {
- IP = cboIP.SelectedItem.ToString();
- }
添加新的客户端form:
- private void btnAddClient_Click(object sender, EventArgs e)
- {
- if (iClientNum == MAXCLIENTNUM)
- {
- MessageBox.Show("用户数已达最大值,无法创建新的客户机!");
- return;
- }
- Form clientForm = new Form2();
- clientForm.Show();
- }
客户端代码
所有全局变量以及含义:
- // 客户端接收服务器消息线程
- private Thread threadClient = null;
- // 服务器socket
- private Socket socketClient = null;
- // 客户名字
- private String clientName = "";
- // 客户端端口
- private int clientPort;
- // 客户端IP
- private IPAddress clientIP = null;
- // 客户端IPEndPoint
- private IPEndPoint clientIpEndPoint = null;
- // 消息缓存区
- byte[] recvBuffer = null;
初始化控件:
- private void initView()
- {
- btnConnectToServer.Enabled = true;
- btnExit.Enabled = false;
- tbMonitor.ReadOnly = true;
- tbIP.ForeColor = Color.Red;
- tbPort.ForeColor = Color.Red;
- tbName.ForeColor = Color.Red;
- }
连接服务器:
- // 连接服务器
- private void btnConnectToServer_Click(object sender, EventArgs e)
- {
- try // 防止输入的信息出现错误 eg:port中含有字母
- {
- clientIP = IPAddress.Parse(tbIP.Text.ToString().Trim());
- clientPort = Int32.Parse(tbPort.Text.ToString().Trim());
- clientName = tbName.Text.ToString().Trim();
- }
- catch(Exception)
- {
- MessageBox.Show("输入信息不合法,请重新输入!");
- return;
- }
- btnConnectToServer.Enabled = false;
- btnExit.Enabled = true;
- clientIpEndPoint = new IPEndPoint(clientIP, clientPort);
- socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- tbIP.ReadOnly = true;
- tbPort.ReadOnly = true;
- tbName.ReadOnly = true;
- try
- {
- socketClient.Connect(clientIpEndPoint);
- }
- catch
- {
- MessageBox.Show("连接失败\r\n");
- btnConnectToServer.Enabled = true;
- btnExit.Enabled = false;
- return;
- }
- threadClient = new Thread(recvMsg);
- threadClient.Start();
- }
接受服务器发来的消息:
- // 接受服务器端发来的消息
- private void recvMsg()
- {
- while(true)
- {
- try //
- {
- // 临时存储接收到的信息 1kb
- recvBuffer = new byte[1024];
- //将客户端套接字接收到的数据存入内存缓冲区,并获取长度
- int length = socketClient.Receive(recvBuffer);
- string strRecvMsg = Encoding.UTF8.GetString(recvBuffer, 0, length);
- addMonitorMessage(strRecvMsg);
- }
- catch
- {
- addMonitorMessage("远程服务器已经中断连接\r\n");
- btnConnectToServer.Enabled = true;
- break;
- }
- }
- }
客户端发送消息:
- private void btnCSend_Click(object sender, EventArgs e)
- {
- try
- {
- string strMsg = tbSendMsg.Text.ToString().Trim();
- if("".Equals(strMsg))
- {
- MessageBox.Show("消息不能为空!");
- return;
- }
- if(btnConnectToServer.Enabled == true)
- {
- MessageBox.Show("请先连接到服务器,再发送消息");
- return;
- }
- tbSendMsg.Text = "";
- strMsg = "[" + clientName + "]:" + strMsg;
- socketClient.Send(Encoding.UTF8.GetBytes(strMsg));
- }
- catch
- {
- MessageBox.Show("消息发送失败,请检查是否连接到服务器");
- return;
- }
- }
断开连接:
- private void btnExit_Click(object sender, EventArgs e)
- {
- try
- {
- // 通知服务器即将断开连接
- socketClient.Send(Encoding.UTF8.GetBytes("EXIT"));
-
- socketClient.Dispose();
- socketClient.Close();
- btnExit.Enabled = false;
- btnConnectToServer.Enabled = true;
- tbName.ReadOnly = false;
- }
- catch
- {
- MessageBox.Show("客户机关闭失败!");
- return;
- }
- }
实验结果
服务器的初始化和启动:
添加客户以及连接服务器:
服务器发送消息:
客户端发送消息:
关闭客户端:
关闭服务器:
问题:
多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,访问窗口的控件时无法对其控制。
解决方法:
将窗口构造函数中的CheckForIllegalCrossThreadCalls设置为false;然后就能安全的访问窗体控件。
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
问题:
服务器创建时输入的ip可能不是本机的ip
解决方法:
将ip输入框改为下拉框,内容是本机所有可用ipv4的ip
问题:
传送消息之后,收到的中文显示乱码
解决方法:
发送消息时使用UTF8编码
clientSocketList[i].Send(Encoding.UTF8.GetBytes(message));
在调试的过程中,巩固提高了针对问题、错误的分析能力和逻辑思维能力。
学会了网络编程的一些基本知识。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。