FTP 编写 3:同时为多个客户端服务(多线程)


    在上篇文章中写了一个能连接的FTP,但是它只能为一个客户端进行服务,而我们知道在现实生活中我们见到的大部分网络应用都能同时为多个用户提供服务,所以接下来我们的目标是使用多线程来使服务端能同时为多个客户端进行服务。

    C++ 多线程这个不是网络编程的内容,所以在这里不会详细讲解(其实我也不懂啊,刚看的书就来瞎写试试了),并且在这里使用的也不会太复杂(应该)。如果各位有对多线程有疑惑或者有兴趣,如果是计算机专业的并且学过操作系统的可以去看这本关于 C++ 多线程的书:《C++ Concurrency in Action》,这本书从浅入深地进行讲解,很详细,有了操作系统关于线程的知识就容易看懂,上手也比较容易,我就是看了这本书才有胆子试试水的。我也不知道如果没有看过操作系统看这个容易不,你们也可以去试一试,如果感觉困难的话看一看操作系统的线程部分,然后再看。

    好了,也就不废话了,下面开始编写

服务端

    首先对服务端进行编写,在上一篇的代码中进行修改添加。添加的部分是多线程部分。

    首先我们需要把多线程部分和 vector 容器的头文件添加进来:

#include <thread>
#include <vector>

    添加相应的变量,考虑到每有一个客户端进行连接我们就需要新开一个线程专门对其进行服务,是动态变化的,所以我们使用指针,并且将线程在后台进行运行,这样我们就不用再对其进行管理,它自个按照代码运行就行了。而考虑到指针的内存释放问题,我们使用 vector 容器将每个新建的线程指针进行保存,在程序结束的时候释放到其占用的内存,相应的代码如下:

//定义多线程指针,用于创建线程
std::thread* t;
//用于线程的管理,保存创建的多线程指针,程序结束时释放占用的内存
std::vector<std::thread*> tManage;//创建新的线程,并加入容器中,并将线程后台运行
t = new std::thread(server, sAccept);//server是线程运行函数,sAccept是已经与客户端连接的套接字
tManage.push_back(t);
t->detach();

    接下来是线程运行函数,就是上面线程创建中的 server:其主要的内容是在第一次连接时候发送连接的信息给客户端,就想上一篇文章中的那样,然后我们用一个无限循环,在循环里面接收客户端发送来的信息并打印出来,具体代码如下:

//多线程函数,创建的多线程运行此函数
void server(SOCKET s) {SOCKET socket = s;struct sockaddr_in ser, cli;//网络地址int iSend, iRecv;char buf[1024] = "I am a server";//显示客户端的 IP 信息char clibuf[20] = { '\0' };inet_ntop(AF_INET, (void*)&cli.sin_addr, clibuf, 16);std::cout << "Accept client IP:" << clibuf << ":" << ntohs(cli.sin_port) << std::endl;//发送信息给客户端iSend = send(socket, buf, sizeof(buf), 0);if (iSend == SOCKET_ERROR) {std::cout << "send() Failed\n";}else if (iSend == 0) {std::cout << "send() Zero\n";}else {std::cout << "Send byte:" << iSend << std::endl;std::cout << "----------------------------------\n";}//使用循环不断接受客户端发送来的信息并显示while (true) {iRecv = recv(socket, buf, sizeof(buf), 0);if (iRecv == 0) {//std::cout << "recv() Zero\n";}else if (iRecv == SOCKET_ERROR) {std::cout << "recv() Failed\n";}else {std::cout << "recv() data from server:" << buf << std::endl;}}
}

客户端

    客户端改动的相应较少,也不需要多线程。在上一篇的程序中客户端接收完服务端发送来的信息以后就关闭了套接字,断开了连接,结束了程序。而我们要测试一下我们的多线程是否有用就不能让它接收完消息后就退出了,所以在接收消息的后面我们加了一个循环让其不断的发送消息:我们定义一个数字,这个数字在循环中不断的递增并发送给服务端。而但我们在不同的时间启动多个客户端程序时,这个数字是不一样的,如果在服务端显示正确,那么我们的多线程就是可以使用的。代码如下:

    //对服务端发送指令或者数据int index = 0;//此数不断递增,然后发送给服务端,用于验证多线程是否有用while (true) {//输入相应的数据发送给服务端//std::cout << ">>";//std::cin >> buf;//将数字转换为字符串_itoa_s(index, buf, 10);++index;//发送数据到服务端iSend = send(sClient, buf, sizeof(buf), 0);if (iSend == SOCKET_ERROR) {std::cout << "send() Failed\n";}else if (iSend == 0) {std::cout << "send() Zero\n";}else {std::cout << "Send byte:" << iSend << std::endl;std::cout << "----------------------------------\n";}}

完整代码如下:

服务端:

#include <Winsock2.h>
#include <Ws2tcpip.h>
#include <iostream>
#include <thread>
#include <vector>
#pragma comment(lib,"ws2_32.lib")//多线程函数,创建的多线程运行此函数
void server(SOCKET s);void main()
{//定义相关的数据int iPort = 5050;//定义其端口WSADATA wsaData;//Winsock 的启动参数SOCKET sListen, sAccept;//套接口关键字,分别用于监听和接收连接int iLen;int iSend;char buf[] = "I am a server";struct sockaddr_in ser, cli;//网络地址//定义多线程指针,用于创建线程std::thread* t;//用于线程的管理,保存创建的多线程指针,程序结束时释放占用的内存std::vector<std::thread*> tManage;std::cout << "----------------------------\n";std::cout << "Server waitting\n";std::cout << "----------------------------\n";//启动winSocketif (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {std::cout << "Failed to load Winsock.\n";return;}//创建SocketsListen = socket(AF_INET, SOCK_STREAM, 0);if (sListen == INVALID_SOCKET) {std::cout << "socket() Failed:" << WSAGetLastError() <<"\n";return;}//绑定IP地址ser.sin_family = AF_INET;ser.sin_port = htons(iPort);ser.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(sListen, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR) {std::cout << "bind() Failed\n";return;}//监听if (listen(sListen, 5) == SOCKET_ERROR) {std::cout << "listen() Failed\n";return;}iLen = sizeof(cli);//获取客户端网络地址的长度//接受连接和发送欢迎信息//用循环使程序一直运行while (true) {//接收连接sAccept = accept(sListen, (struct sockaddr*)&cli, &iLen);if (sAccept == INVALID_SOCKET) {std::cout << "accept() Failed\n";break;}//创建新的线程,并加入容器中,并将线程后台运行t = new std::thread(server, sAccept);tManage.push_back(t);t->detach();}//释放指针占用的内存for (int i = 0; i < tManage.size(); i++) {delete(tManage[i]);}//关闭监听closesocket(sListen);//关闭 WinsockWSACleanup();
}//多线程函数,创建的多线程运行此函数
void server(SOCKET s) {SOCKET socket = s;struct sockaddr_in ser, cli;//网络地址int iSend, iRecv;char buf[1024] = "I am a server";//显示客户端的 IP 信息char clibuf[20] = { '\0' };inet_ntop(AF_INET, (void*)&cli.sin_addr, clibuf, 16);std::cout << "Accept client IP:" << clibuf << ":" << ntohs(cli.sin_port) << std::endl;//发送信息给客户端iSend = send(socket, buf, sizeof(buf), 0);if (iSend == SOCKET_ERROR) {std::cout << "send() Failed\n";}else if (iSend == 0) {std::cout << "send() Zero\n";}else {std::cout << "Send byte:" << iSend << std::endl;std::cout << "----------------------------------\n";}//使用循环不断接受客户端发送来的信息并显示while (true) {iRecv = recv(socket, buf, sizeof(buf), 0);if (iRecv == 0) {//std::cout << "recv() Zero\n";}else if (iRecv == SOCKET_ERROR) {std::cout << "recv() Failed\n";}else {std::cout << "recv() data from server:" << buf << std::endl;}}
}

客户端

#include <Winsock2.h>
#include <WS2tcpip.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")void main()
{//定义相应的数据WSADATA wsaData;SOCKET sClient;int iPort = 5050;//对应的服务端的端口int iLen, iSend;char buf[1024];struct sockaddr_in ser;//启动 winSocketmemset(buf, 0, sizeof(buf));if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {std::cout << "Failed to load Winsock\n";system("pause");return;}//char addr[20] = { '\0' };//std::cin >> addr;//输入服务端 IP 地址char addr[20] = "192.168.1.119";//创建Socketser.sin_family = AF_INET;ser.sin_port = htons(iPort);inet_pton(AF_INET, addr, (void*)&ser.sin_addr.s_addr);sClient = socket(AF_INET, SOCK_STREAM, 0);if (sClient == INVALID_SOCKET) {std::cout << "socket() Failed\n";system("pause");return;}//连接并进行简单的操作if (connect(sClient, (struct sockaddr*)&ser, sizeof(ser)) == INVALID_SOCKET) {std::cout << "connect() Failed\n";system("pause");return;}else {//接收服务端发送的数据iLen = recv(sClient, buf, sizeof(buf), 0);if (iLen == 0) {system("pause");return;}else if (iLen == SOCKET_ERROR) {std::cout << "recv() Failed\n";system("pause");return;}std::cout << "recv() data from server:" << buf << std::endl;}//对服务端发送指令或者数据int index = 0;//此数不断递增,然后发送给服务端,用于验证多线程是否有用while (true) {//输入相应的数据发送给服务端//std::cout << ">>";//std::cin >> buf;//将数字转换为字符串_itoa_s(index, buf, 10);++index;//发送数据到服务端iSend = send(sClient, buf, sizeof(buf), 0);if (iSend == SOCKET_ERROR) {std::cout << "send() Failed\n";}else if (iSend == 0) {std::cout << "send() Zero\n";}else {std::cout << "Send byte:" << iSend << std::endl;std::cout << "----------------------------------\n";}}//关闭连接并退出closesocket(sClient);//关闭 WinsockWSACleanup();system("pause");
}

运行

    首先启动服务端,然后启动多个客户端,因为启动的时间不一样,所以各个客户端在服务端显示的数字是相差还是比较大的

FTP 编写 3:同时为多个客户端服务(多线程)相关推荐

  1. FTP 编写 4:命令解析

    FTP 编写 4:命令解析     我们知道在现实生活中使用的 FTP 是应答式的,客户端和服务端按照一定的规定进行交流,不是随便弄的,在上几篇中的 FTP 没有人机交互的功能.所以这篇文章的主要内容 ...

  2. FTP 编写 2:客户端与服务端的连接

    FTP 编写 2:客户端与服务端的连接     首先编写客户端与服务端能进行简单的连接,这个较为容易:     服务端的编写流程是:启动 Winsock.建立套接字.绑定套接字.监听.接收连接.关闭连 ...

  3. java编写的王八程序_利用JAVA多线程技术模拟龟兔赛跑.doc

    利用JAVA多线程技术模拟龟兔赛跑 摘要:该文介绍了利用JAVA语言的多线程技术,对"龟兔赛跑"寓言故事的模拟.从模拟程序的具体设计思路,到详细的实现过程,将技术的应用融入到一个有 ...

  4. Linux 多线程应用中编写安全的信号处理函数

    2019独角兽企业重金招聘Python工程师标准>>> Linux 多线程应用中编写安全的信号处理函数 在 开发多线程应用时,开发人员一般都会考虑线程安全,会使用 pthread_m ...

  5. c++ sleep函数_Linux 多线程应用中如何编写安全的信号处理函数

    关于代码的可重入性,设计开发人员一般只考虑到线程安全,异步信号处理函数的安全却往往被忽略.本文首先介绍如何编写安全的异步信号处理函数:然后举例说明在多线程应用中如何构建模型让异步信号在指定的线程中以同 ...

  6. Linux 多线程应用中如何编写安全的信号处理函数

    Linux 多线程应用中编写安全的信号处理函数 在开发多线程应用时,开发人员一般都会考虑线程安全,会使用 pthread_mutex 去保护全局变量.如果应用中使用了信号,而且信号的产生不是因为程序运 ...

  7. MPI并行程序编写与调试(奇偶变换排序)

    MPI并行程序编写与调试(奇偶变换排序) 一.编写MPI并行计算程序 奇偶排序在多线程进行计算代码如下: 关于奇偶排序这里不做过多描述. /** File: mpi_odd_even.c* Purpo ...

  8. 【Alljoyn】 Alljoyn学习笔记七 Alljoyn瘦客户端库介绍

    Alljoyn瘦客户端库介绍(上) 1.简介 本文档对AllJoynTM瘦客户端的核心库文件(AJTCL)进行了详尽的介绍.本文档介绍了系统整体架构,AllJoyn框架结构,并着重于介绍如何将嵌入式设 ...

  9. Linux网络——部署yum仓库

    YUM的部署 一.YUM 仓库 1.1 YUM概述 1.2 YUM常用命令 1.2.3 查询软件包命令 1.2.2 查询软件包组命令 1.2.3 yum安装升级 1.2.4 软件卸载 二.YUM仓库的 ...

最新文章

  1. Vue @import ‘~@/css/reset.css’;报错,解决方案
  2. IDEA/Git 设置多个push远程仓库或者同时提交多个push仓库
  3. html中透明度怎么写,css中控制透明度
  4. Python for else 的使用(银行账号的登录)
  5. 阅读替换净化规则_usmile电动牙刷头适配Y1/Y4/45度小白刷大理石一号刷P1替换激泡...
  6. mysql ignore 1 lines_MYSQL使用笔记(1)
  7. 二分归并排序算法_第五篇排序算法|归并排序
  8. NSThread使用总结
  9. 莫烦python博客_《莫烦Python》笔记 -- numpy部分
  10. UnicodeDecodeError: 'gbk' codec can't decode byte 0xb4 in position xx
  11. spring boot +spring security + jwt 实现认证模块
  12. 【软考】软考简易版知识点复习指南汇总
  13. 华中师范大学邮箱matlab,正版软件管理与服务平台(华中师范大学)
  14. Linux虚拟内存那点事儿
  15. 1、曾经风光无限的jsp,为什么现在很少有人使用了?
  16. SPSS单因素方差分析流程 分析某因素对试验结果是否有显著影响
  17. python通过qq邮箱发邮件
  18. Python斐波那契数列算法
  19. android usb单反相机,android mtp 获取单反相机中的照片
  20. 基于单片机模拟打地鼠游戏设计-protues仿真毕业

热门文章

  1. JS组件系列——又一款MVVM组件:Vue(一:30分钟搞定前端增删改查)
  2. 关于Cococs中的CCActionEase(下)
  3. asp.net 子域跨域 带cookie
  4. C/C++的流(stream)对象
  5. 转载--ASP解决AJAX带来的码问题
  6. epoll 性能分析(解决占用CPU 过高问题)2
  7. 解决Ubuntu刚装好的时候su命令密码错误的问题
  8. 【白皮书分享】2022年私域运营趋势及创业机会.pdf(附下载链接)
  9. 【免费下载】2021年9月热门报告盘点(附热门报告列表及下载链接)
  10. 谷歌 ICLR 2020 | 向量化召回也需要『预训练』