网络编程基础第四讲阻塞模型
网络编程基础第四讲阻塞模型
网络编程基础第四讲阻塞模型
一丶阻塞模型简介
不知道大家有没有注意到.我们客户端 或者服务端.的TCP 收发数据的时候(send/recv)如果接受不到数据就一直不返回.从而造成我们网络的阻塞.程序无法正常执行.
不过针对这一方法.我们可以开一个线程去专门接受数据.或者发送数据.
这个就是我们常说的阻塞.
只要我们创建的套接字都是阻塞模型. 就是说数据接受不到不返回.
我们可以设置为非阻塞.就是不管数据有没有来到都会返回.如果来到.会有通知.我们可以编程接受数据.
设置非阻塞模式方法
ioctlsocket(SOCKET s, long cmd, u_long *arpg);
改变套接字模式.为飞租she.
二丶阻塞模式迭代模式 与 并发连接模式
1.阻塞模式的迭代模式 就是指每次只服务一个连接.只有服务完当前的客户端连接之后.才会继续服务下一个客户连接
2.并发连接模式 通过多线程.可以同时服务多个链接.没一个线程处理一个客户端的连接.
阻塞迭代模式步骤
1.生成一个函数.绑定本地地址跟监听.
2.生成一个函数.专门接受一个客户端连接.并且返回对应连接的套接字.
3.处理没一个客户端的连接.实现接受跟发送数据.
4.关闭一个连接.
其实就是讲创建服务端网络做了一个封装.
如下代码. 一个.h文件.存放函数声明.一个.cpp封装了网络连接的代码.
.h文件:
#pragma once #include "stdafx.h" #include <WinSock2.h> #include <iostream> #pragma comment(lib,"ws2_32.lib") using namespace std; #include "initSocket.h" void DebugLog(TCHAR *str); //初始化数据 int initSocket(); //1.创建套接字.绑定地址,开始监听 SOCKET BindAnListen(int nBacklog); //接受连接分装 SOCKET AccepeConnect(SOCKET hSocket); //接受跟发送数据 BOOL ClientReadAnWriteData(SOCKET hSocket); //关闭数据连接 BOOL ColseConnect(SOCKET hSocket);
.cpp实现.
#include "initSocket.h" void DebugLog(TCHAR *str) { cout << str << WSAGetLastError() << endl; } //初始化数据 int initSocket() { WSADATA data; if (WSAStartup(MAKEWORD(2, 2), &data)) { DebugLog(TEXT("initsocket faile")); return 0; } } //1.创建套接字.绑定地址,开始监听 SOCKET BindAnListen(int nBacklog) { //创建套接字 BOOL bRet = FALSE; SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == hSocket) { DebugLog(TEXT("BindAnListen Fail")); return INVALID_SOCKET; } //绑定套接字 sockaddr_in addr; addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //htonl addr_any addr.sin_family = AF_INET; addr.sin_port = htons(8524); bRet = bind(hSocket, (sockaddr *)&addr, sizeof(addr)); if (SOCKET_ERROR == bRet) { DebugLog(TEXT("bind fail")); closesocket(hSocket); WSACleanup(); return INVALID_SOCKET; } //监听套接字 bRet = FALSE; bRet = listen(hSocket, nBacklog); if (SOCKET_ERROR ==bRet) { DebugLog(TEXT("Listen fail")); closesocket(hSocket); WSACleanup(); return INVALID_SOCKET; } return hSocket; } //接受连接分装 SOCKET AccepeConnect(SOCKET hSocket) { sockaddr_in addr; int nSize = sizeof(addr); SOCKET hNewSocket = accept(hSocket, (LPSOCKADDR)&addr, &nSize); if (hNewSocket == INVALID_SOCKET) { DebugLog(TEXT("Accept An Connect Fail")); return INVALID_SOCKET; } return hNewSocket; } //接受跟发送数据 BOOL ClientReadAnWriteData(SOCKET hSocket) { char szBuffer[1024] = { NULL }; int nBufferSzie = sizeof(szBuffer); //循环处理数据 int nRecvBytes = 0; do { nRecvBytes = recv(hSocket,szBuffer, nBufferSzie, 0); if (SOCKET_ERROR == nRecvBytes) { DebugLog(TEXT("Recv Data Fail")); return FALSE; } else if (0 != nRecvBytes) {
szBuffer[nRecvBytes] = 0; cout << "接受到的数据为: " << szBuffer << endl; //接着循环发送回去. int nSendDataBytes = 0; while (nSendDataBytes < nRecvBytes) { int nRetValue = send(hSocket, szBuffer, nBufferSzie, 0); if (nRetValue > 0) { nSendDataBytes = nSendDataBytes + nRetValue; //每次发送的数据都增加.这样就会发送过去了 } else if (nRetValue == SOCKET_ERROR) { DebugLog(TEXT("发送数据失败")); return FALSE; } else { //send 返回0 也就是send失败了.客户端关闭了 DebugLog(TEXT("发送数据失败,客户端已经关闭了")); return FALSE; } } } } while (0 != nRecvBytes); return FALSE; } BOOL ColseConnect(SOCKET hSocket) { //shutdown 跟 closesocket一样.不过 TCP 会发送一个FIN分段.给对方表名已经完成数据发送 if (shutdown(hSocket,SD_SEND) == SOCKET_ERROR) { DebugLog(TEXT("关闭连接失败")); return FALSE; } //注意.客户端会发送一个数据.不写也可以. return TRUE; }
上面的代码只是把我们网络创建的一些步骤给封装了.并没有实际编写我们用的代码.
在main函数中使用.只服务一个socket操作
// Server.cpp : 定义控制台应用程序的入口点。 // #include "initSocket.h" int main() { //初始化 initSocket(); //1.绑定并且监听 SOCKET hSocket = BindAnListen(1); if (INVALID_SOCKET == hSocket) { DebugLog(TEXT("main Bind Fail")); goto Opt; } // 2.循环接受套接字连接 while (true) //主要代码是这里. { //接受客户端连接 SOCKET hRetSocket = AccepeConnect(hSocket); if (INVALID_SOCKET == hRetSocket) { DebugLog(TEXT("main accept Fail")); break; } //读取数据. if (FALSE == ClientReadAnWriteData(hRetSocket)) { //只服务一个socket.对其进行读取写入操作.然后下方进行关闭. break; } if (ColseConnect(hRetSocket)) { break; } } Opt: getchar(); //等待一下.观看错误内容 ColseConnect(hSocket); return 0; }
主要就是服务端的代码.客户端进行发送数据即可.