Socket编程

要想客户端和服务器能在网络中通信,那必须得使用 Socket 编程。

服务端首先调用 socket() 函数,创建网络协议为 IPv4,以及传输协议为 TCPSocket ,接着调用 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;
}



参考文献

  1. Windows网络编程, socket开发
  2. I/O多路复用

Windows下网络编程及多线程模型相关推荐

  1. Windows Sockets网络编程(5)完成端口模型(IOCP)

    摘要:上篇文章<Windows Sockets网络编程(4)套接字重叠IO模型>中,讲到了重叠IO的模型,同时也提到了APC函数,重叠IO是通过注册APC函数让线程调用来实现的,细心的你会 ...

  2. gil php,网络编程之多线程——GIL全局解释器锁

    网络编程之多线程--GIL全局解释器锁 一.引子 定义: In CPython, the global interpreter lock, or GIL, is a mutex that preven ...

  3. Linux下网络编程

    Linux下网络编程初步 Linux以其源代码公开闻名于世,并以其稳定性和可靠性雄霸操作系统领域,在网络应用技术方面使用得更加广泛.很久以来它就是Windows的重要对手之一.随着网络时代的来临,Li ...

  4. Java网络编程案例--CS模型的简单实现

    Java网络编程案例–CS模型的简单实现 Java网络编程案例CS模型的简单实现 基本概述 程序原理图 源代码 基本概述 该程序采用C/S模型,在服务器端简单的建立了一个多线程类,来实现对多个客户端传 ...

  5. 使用Dev C++进行Windows socket网络编程,需链接lws2_32库

    背景 在我们使用Dev C++进行C语言编程时,如果我们引入的库是C语言标准库,那我们是不要在编译器选项中进行额外的设置的,但是如果我们使用的是一些不是C语言标准库,那我们可能就需要在编译器选择中进行 ...

  6. Windows下网络数据报的监听和拦截技术

    Windows下网络数据报的监听和拦截技术是一个比较古老的话题,应用也很广泛,例如 防火墙等等.这篇小文只是对该技术的一个总结,没有新技术,高手免看:) 要监听和拦截Windows下的数据报,基本可以 ...

  7. Windows下Socket编程

    Windows下Socket编程 构架 创建socket 绑定bind 存储转换函数 监听listen 接收accept 发送send(tcp)/sendto(udp) 接收recv(tcp)/rec ...

  8. windows下socket编程GetLastError()函数返回结果与对照表-转

    原文地址:http://blog.sina.com.cn/s/blog_4880c4bb0100b6a5.html WSAGetLastError()函数返回结果与对照表: Windows Socke ...

  9. 网络编程I/O模型分析

    网络编程I/O模型分析 1.阻塞I/O.非阻塞I/O模型 阻塞IO模型 非阻塞型 2.I/O复用模型 slecet() pool() epool() ==结构体== ==epoll_create()= ...

最新文章

  1. Spring Boot(四)Accessing application arguments
  2. 后端根据百度地图真实路径距离_远场语音识别错误率降低30%,百度提基于复数CNN网络的新技术...
  3. 周志华领衔撰写,历时4年,宝箱书问世!文末送书
  4. cobbler get-loaders 通过代理下载
  5. python生物数据分析_Python学生物统计-数据可视化-学习笔记5
  6. linux执行命令提示缺少so,Linux软件缺少动态链接库.so怎么办
  7. 单片机,微控制器和微处理器的主要区别?
  8. JavaWeb——jdbc与dbcp数据库连接
  9. 蓝宝石rx470d原版bios_蓝宝石显卡等级划分,如何区分双胞胎矿卡,旗舰值得入手吗?...
  10. 千里达v1000时速_重新定义性价比 千里达V1000碳架山地车 评测
  11. 智能控制导论 # 模糊控制 2 模糊控制器的原理与设计方法
  12. Python 3——xlsxwriter生成图表
  13. html给表格添加标题栏,word表格怎么添加标题栏 如何在word表格上方加标题
  14. 1.从第一道面试题谈起
  15. java 重载与重写 【转】
  16. 前端面试题精编2020(js、html、小程序、React、ES6、Vue、算法、全栈热门视频资源)持续更新
  17. Android4.3 Google Pinyin输入法UI定制
  18. U盘插电脑有提示音但不显示盘符怎么办?
  19. 结合GIS+BIM数字孪生应用,将构建智慧综合管廊工程三维渲染新高地
  20. 一行代码解决从Mysql数据库取出datetime时间少八小时的问题

热门文章

  1. echarts入门 堆叠折线图
  2. matlab图形功能(二维图形和三维图像)
  3. linux不解压查看gz文件内容,不解压查看tar.gz文件的大小
  4. 【工善事,必利器】日程
  5. Redis.conf redis6配置文件详解
  6. Windows服务的安装与卸载
  7. QString 转换 Wchar_t方法比对
  8. 几款名壶的来历(图片)
  9. uview——uniapp最优秀的UI框架
  10. 客户端渲染和服务端渲染