Windows下网络编程及多线程模型
Socket编程
要想客户端和服务器能在网络中通信,那必须得使用 Socket
编程。
服务端首先调用 socket()
函数,创建网络协议为 IPv4
,以及传输协议为 TCP
的 Socket
,接着调用 bind()
函数,给这个 Socket
绑定一个 IP
地址和端口。然后,服务端可以调用 listen()
函数进行监听,进入了监听状态后,通过调用 accept()
函数,来从内核获取客户端的连接,如果没有客户端连接,则会阻塞等待客户端连接的到来。
客户端在创建好 Socket
后,调用 connect()
函数发起连接,该函数的参数要指明服务端的 IP
地址和端口号,接着就是开始三次握手。
在 TCP
连接的过程中,服务器的内核实际上为每个 Socket
维护了两个队列:
- 半连接队列
- 全连接队列
当 TCP
全连接队列不为空后,服务端的 accept()
函数,就会从内核中的 TCP
全连接队列里拿出一个已经完成连接的 Socket
返回应用程序,后续数据传输都用这个 Socket
。因此,进行三次握手的Socket
和传输数据的Socket
是不一样的。
连接建立后,客户端和服务端就开始相互传输数据了,双方都可以通过 read()
和 write()
函数来读写数据。
下面看一下同步阻塞的方式实现的Socket
通信。将两份cpp文件放在不同工程中,分别生成。
server.cpp
#include <stdio.h>
#include <windows.h>#pragma comment(lib, "ws2_32.lib")int main()
{//1. 请求协议版本WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {printf("request failed!\n");return -1;}printf("request protocol success!\n");//2. 创建SocketSOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (serverSocket == SOCKET_ERROR) {printf("create failed!\n");WSACleanup();return -2;}printf("create socket success!\n");//3.创建协议族SOCKADDR_IN addr = { 0 };addr.sin_family = AF_INET; //协议版本addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.127");addr.sin_port = htons(10086);//0-65535 10000左右//4.绑定int r = bind(serverSocket, (sockaddr*)&addr, sizeof addr);if (-1 == r) {printf("bind failed!\n");closesocket(serverSocket);WSACleanup();return -2;}printf("bind success!\n");//5.监听r = listen(serverSocket, 10);if (-1 == r) {printf("listen failed!\n");closesocket(serverSocket);WSACleanup();return -2;}printf("listen success!\n");//6.等待客户端连接 阻塞 SOCKADDR_IN cAddr = { 0 };int len = sizeof(cAddr);SOCKET clientSocket = accept(serverSocket, (sockaddr*)&cAddr, &len);if (SOCKET_ERROR == clientSocket) {printf("serverd failed!\n");closesocket(clientSocket);WSACleanup();return -2;}printf("one client(%s) has connected server!\n", inet_ntoa(cAddr.sin_addr));//7.通信char buff[1024];while (1) {r = recv(clientSocket, buff, 1023, NULL);if (r > 0) {buff[r] = 0; // 添加'\0'printf(">>%s\n", buff);}}return 0;
}
client.cpp
#include <stdio.h>
#include <windows.h>#pragma comment(lib, "ws2_32.lib")int main()
{//1. 请求协议版本WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {printf("request failed!\n");return -1;}printf("request protocol success!\n");//2. 创建SocketSOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (clientSocket == SOCKET_ERROR) {printf("create failed!\n");WSACleanup();return -2;}printf("create socket success!\n");//3.获取服务器协议族SOCKADDR_IN addr = { 0 };addr.sin_family = AF_INET; //协议版本addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.127");addr.sin_port = htons(10086);//0-65535 10000左右//4.连接服务器int r = connect(clientSocket, (sockaddr*)&addr, sizeof addr);if (r == -1) {printf("connecting server failed!\n");return -1;}printf("connecting server success!\n");//5.通信char buff[1024];while (1) {memset(buff, 0, 1024);printf("please enter your words: ");gets_s(buff);r = send(clientSocket, buff, strlen(buff), NULL);}return 0;
}
通信效果
多线程模型
同步阻塞的方式只能让服务器服务一个客户端,当服务器没有结束当前客户端的网络I/O
时,其他客户端是无法连接服务器的。因此,我们可以使用多线程模型来处理多客户端的请求。
server.cpp
#include <stdio.h>
#include <windows.h>#pragma comment(lib, "ws2_32.lib")SOCKADDR_IN cAddr = { 0 };
int len = sizeof(cAddr);
SOCKET clientSocket[1024];
int count = 0;void communication(int idx) {char buff[1024];int r;while (1) {r = recv(clientSocket[idx], buff, 1023, NULL);if (r > 0) {buff[r] = 0;printf("%d:%s\n", idx, buff);//广播数据for (int i = 0; i < count; i++) {send(clientSocket[i], buff, strlen(buff), NULL);}}}
}int main()
{//1. 请求协议版本WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {printf("request failed!\n");return -1;}printf("request protocol success!\n");//2. 创建SocketSOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (serverSocket == SOCKET_ERROR) {printf("create failed!\n");WSACleanup();return -2;}printf("create socket success!\n");//3.创建协议族SOCKADDR_IN addr = { 0 };addr.sin_family = AF_INET; //协议版本addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.127");addr.sin_port = htons(10086);//0-65535 10000左右//4.绑定int r = bind(serverSocket, (sockaddr*)&addr, sizeof addr);if (-1 == r) {printf("bind failed!\n");closesocket(serverSocket);WSACleanup();return -2;}printf("bind success!\n");//5.监听r = listen(serverSocket, 10);if (-1 == r) {printf("listen failed!\n");closesocket(serverSocket);WSACleanup();return -2;}printf("listen success!\n");//6.等待客户端连接 阻塞 while (1) {clientSocket[count] = accept(serverSocket, (sockaddr*)&cAddr, &len);if (SOCKET_ERROR == clientSocket[count]) {printf("serverd failed!\n");closesocket(serverSocket);WSACleanup();return -2;}printf("one client(%s) has connected server!\n", inet_ntoa(cAddr.sin_addr));CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)communication, (char*)count, NULL, NULL);count++;}//7.通信char buff[1024];while (1) {r = recv(clientSocket[count], buff, 1023, NULL);if (r > 0) {buff[r] = 0; // 添加'\0'printf(">>%s\n", buff);}}return 0;
}
client.cpp
#include <stdio.h>#include <graphics.h> //easyX#pragma comment(lib, "ws2_32.lib")SOCKET clientSocket;
HWND hWnd;int count = 0;void received() {char recvBuff[1024];int r;while (1) {r = recv(clientSocket, recvBuff, 1023, NULL);if (r > 0) {recvBuff[r] = 0;outtextxy(0, count * 20, recvBuff);count++;}}
}int main()
{initgraph(300, 400, SHOWCONSOLE);//1. 请求协议版本WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {printf("request failed!\n");return -1;}printf("request protocol success!\n");//2. 创建SocketclientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (clientSocket == SOCKET_ERROR) {printf("create failed!\n");WSACleanup();return -2;}printf("create socket success!\n");//3.获取服务器协议族SOCKADDR_IN addr = { 0 };addr.sin_family = AF_INET; //协议版本addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.127");addr.sin_port = htons(10086);//0-65535 10000左右//4.连接服务器int r = connect(clientSocket, (sockaddr*)&addr, sizeof addr);if (r == -1) {printf("connecting server failed!\n");return -1;}printf("connecting server success!\n");//5.通信char buff[1024];CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)received, NULL, NULL, NULL);while (1) {memset(buff, 0, 1024);printf("please enter your words: ");gets_s(buff);r = send(clientSocket, buff, strlen(buff), NULL);}return 0;
}
参考文献
- Windows网络编程, socket开发
- I/O多路复用
Windows下网络编程及多线程模型相关推荐
- Windows Sockets网络编程(5)完成端口模型(IOCP)
摘要:上篇文章<Windows Sockets网络编程(4)套接字重叠IO模型>中,讲到了重叠IO的模型,同时也提到了APC函数,重叠IO是通过注册APC函数让线程调用来实现的,细心的你会 ...
- gil php,网络编程之多线程——GIL全局解释器锁
网络编程之多线程--GIL全局解释器锁 一.引子 定义: In CPython, the global interpreter lock, or GIL, is a mutex that preven ...
- Linux下网络编程
Linux下网络编程初步 Linux以其源代码公开闻名于世,并以其稳定性和可靠性雄霸操作系统领域,在网络应用技术方面使用得更加广泛.很久以来它就是Windows的重要对手之一.随着网络时代的来临,Li ...
- Java网络编程案例--CS模型的简单实现
Java网络编程案例–CS模型的简单实现 Java网络编程案例CS模型的简单实现 基本概述 程序原理图 源代码 基本概述 该程序采用C/S模型,在服务器端简单的建立了一个多线程类,来实现对多个客户端传 ...
- 使用Dev C++进行Windows socket网络编程,需链接lws2_32库
背景 在我们使用Dev C++进行C语言编程时,如果我们引入的库是C语言标准库,那我们是不要在编译器选项中进行额外的设置的,但是如果我们使用的是一些不是C语言标准库,那我们可能就需要在编译器选择中进行 ...
- Windows下网络数据报的监听和拦截技术
Windows下网络数据报的监听和拦截技术是一个比较古老的话题,应用也很广泛,例如 防火墙等等.这篇小文只是对该技术的一个总结,没有新技术,高手免看:) 要监听和拦截Windows下的数据报,基本可以 ...
- Windows下Socket编程
Windows下Socket编程 构架 创建socket 绑定bind 存储转换函数 监听listen 接收accept 发送send(tcp)/sendto(udp) 接收recv(tcp)/rec ...
- windows下socket编程GetLastError()函数返回结果与对照表-转
原文地址:http://blog.sina.com.cn/s/blog_4880c4bb0100b6a5.html WSAGetLastError()函数返回结果与对照表: Windows Socke ...
- 网络编程I/O模型分析
网络编程I/O模型分析 1.阻塞I/O.非阻塞I/O模型 阻塞IO模型 非阻塞型 2.I/O复用模型 slecet() pool() epool() ==结构体== ==epoll_create()= ...
最新文章
- Spring Boot(四)Accessing application arguments
- 后端根据百度地图真实路径距离_远场语音识别错误率降低30%,百度提基于复数CNN网络的新技术...
- 周志华领衔撰写,历时4年,宝箱书问世!文末送书
- cobbler get-loaders 通过代理下载
- python生物数据分析_Python学生物统计-数据可视化-学习笔记5
- linux执行命令提示缺少so,Linux软件缺少动态链接库.so怎么办
- 单片机,微控制器和微处理器的主要区别?
- JavaWeb——jdbc与dbcp数据库连接
- 蓝宝石rx470d原版bios_蓝宝石显卡等级划分,如何区分双胞胎矿卡,旗舰值得入手吗?...
- 千里达v1000时速_重新定义性价比 千里达V1000碳架山地车 评测
- 智能控制导论 # 模糊控制 2 模糊控制器的原理与设计方法
- Python 3——xlsxwriter生成图表
- html给表格添加标题栏,word表格怎么添加标题栏 如何在word表格上方加标题
- 1.从第一道面试题谈起
- java 重载与重写 【转】
- 前端面试题精编2020(js、html、小程序、React、ES6、Vue、算法、全栈热门视频资源)持续更新
- Android4.3 Google Pinyin输入法UI定制
- U盘插电脑有提示音但不显示盘符怎么办?
- 结合GIS+BIM数字孪生应用,将构建智慧综合管廊工程三维渲染新高地
- 一行代码解决从Mysql数据库取出datetime时间少八小时的问题