TCP阻塞模式开发
在阻塞模式下,在IO操作完成前,执行的操作函数将一直等候而不会立刻返回,该函数所在的进程会阻塞在这里。相反,在非阻塞模式下,套接字函数会立即返回,而不管IO是否完成,该函数所在的线程将继续运行。阻塞模式编程简单,但效率低;非阻塞模式编程复杂,但效率高,比如:
举个简单的例子,你点击一个发送按钮:
如果是阻塞式的,那么在发送未完成之前,界面就会"卡死"。
而非阻塞式的,无论数据有没有完成,你都继续进行界面的操作
server:
// Server.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <WINSOCK2.H>
#pragma comment(lib, "wsock32.lib")
using namespace std;
#define SERVER_EXIT_OK 0 //服务器正常退出
#define SERVER_DLL_REEOR 1 //调用Windows sockets DLL失败
#define SERVER_API_ERROR 2 //调用Windows sockets API失败
#define SERVERPORT 5555//服务器TCP端口
#define MAX_NUM_BUF 64 //缓冲区最大尺寸
//变量
char bufRecv[MAX_NUM_BUF]; //读缓冲区
char bufSend[MAX_NUM_BUF]; //写缓冲区
SOCKET sServer; //服务器监听套接字
SOCKET sClient; //接受客户端套接字
BOOL bConning; //与客户端的连接状态
//函数
void InitMember(void); //初始化成员变量
int ExitClient(int nExit); //客户端退出
BOOL RecvLine(SOCKET s, char* buf); //读取一行数据
BOOL SendLine(SOCKET s, char* buf); //发送一行数据
int HandleSocketError(char *str); //对Windows sockets API调用错误处理
void ShowSocketMsg(char* str); //显示错误信息
//主函数
int main(int argc, char* argv[])
{
InitMember(); //初始化变量
WORD wVersionRequested; //应用程序需要Windows sockets DLL的版本
WSADATA wsaData; //Windows sockets DLL版本信息
int retVal; //调用Windows sockets API返回值
//初始化Windows Sockets DLL
wVersionRequested = MAKEWORD(1,1);
retVal = WSAStartup(wVersionRequested, &wsaData);
if ( 0 != retVal )
{
ShowSocketMsg("Can not find a usable Windows Sockets dll!");
return SERVER_DLL_REEOR;
}
//确保WinSock DLL支持1.1
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1)
{
ShowSocketMsg("Can not find a usable Windows Sockets dll!");
WSACleanup( );
return SERVER_DLL_REEOR;
}
//创建套接字
sServer= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == sServer)
{
return HandleSocketError("Failed socket()!");
}
//服务器套接字地址
SOCKADDR_IN addrServ;
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(SERVERPORT);
addrServ.sin_addr.s_addr = INADDR_ANY;
//绑定套接字
retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
if(SOCKET_ERROR == retVal)
{
closesocket(sServer); //关闭套接字
return HandleSocketError("Failed bind()!"); //错误处理
}
//开始监听
retVal = listen(sServer, 1);
if(SOCKET_ERROR == retVal)
{
closesocket(sServer); //关闭套接字
return HandleSocketError("Failed listen()!");//错误处理
}
//等待客户端的连接
cout << "Server succeeded!" << endl;
cout << "Waiting for new clients..." << endl;
//接受客户端请求
sockaddr_in addrClient;
int addrClientlen = sizeof(addrClient);
sClient = accept(sServer,(sockaddr FAR*)&addrClient, &addrClientlen);
if(INVALID_SOCKET == sClient)
{
closesocket(sServer); //关闭套接字
return HandleSocketError("Failed accept()!"); //错误处理
}else{
bConning = TRUE; //客户端请求成功
}
//显示客户端的IP和端口
char *pClientIP = inet_ntoa(addrClient.sin_addr);
u_short clientPort = ntohs(addrClient.sin_port);
cout<<"Accept a client."<<endl;
cout<<"IP: "<<pClientIP<<endl;
cout<<"Port: "<<clientPort<<endl;
//接收客户端数据
if (!RecvLine(sClient, bufRecv))
{
return ExitClient(SERVER_API_ERROR);//退出
}
//显示客户端数据
cout << bufRecv<<endl;
//向客户端发送数据
strcpy(bufSend, "Hello,Client!\n");
if (!SendLine(sClient, bufSend))
{
return ExitClient(SERVER_API_ERROR);
}
//显示退出信息
cout << "Server exiting..." << endl;
//退出
return ExitClient(SERVER_EXIT_OK);
}
/*
* 初始化成员变量
*/
void InitMember(void)
{
//初始化读和写缓冲区
memset(bufRecv, 0, MAX_NUM_BUF);
memset(bufSend, 0, MAX_NUM_BUF);
//初始化
sServer = INVALID_SOCKET;
sClient = INVALID_SOCKET;
//没有连接状态
bConning = FALSE;
}
/*
* 退出
*/
int ExitClient(int nExit)
{
closesocket(sServer); //关闭监听套接字
closesocket(sClient); //关闭连接客户端套接接
WSACleanup(); //卸载Windows sockets DLL 清理内存
return nExit; //退出
}
/*
* 读一行数据
*/
BOOL RecvLine(SOCKET s, char* buf)
{
BOOL retVal = TRUE; //返回值
BOOL bLineEnd = FALSE; //行结束
int nReadLen = 0; //读入字节数
int nDataLen = 0; //数据长度
while (!bLineEnd && bConning) //与客户端连接 没有换行
{
nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节
//错误处理
if (SOCKET_ERROR == nReadLen)
{
int nErrCode = WSAGetLastError();//错误代码
if (WSAENOTCONN == nErrCode)
{
ShowSocketMsg("The socket is not connected!");
}else if(WSAESHUTDOWN == nErrCode)
{
ShowSocketMsg("The socket has been shut down!");
}else if (WSAETIMEDOUT == nErrCode)
{
ShowSocketMsg("The connection has been dropped!");
}else if (WSAECONNRESET == nErrCode)
{
ShowSocketMsg("The virtual circuit was reset by the remote side!");
}else{}
retVal = FALSE; //读数据失败
break; //跳出循环
}
if (0 == nReadLen)//客户端关闭
{
retVal = FALSE; //读数据失败
break ; //跳出循环
}
//读入数据
if ('\n' == *(buf + nDataLen)) //换行符
{
bLineEnd = TRUE; //接收数据结束
}else{
nDataLen += nReadLen; //增加数据长度
}
}
return retVal;
}
/*
* //发送一行数据
*/
BOOL SendLine(SOCKET s, char* str)
{
int retVal;//返回值
retVal = send(s, str, strlen(str), 0);//一次发送
//错误处理
if (SOCKET_ERROR == retVal)
{
int nErrCode = WSAGetLastError();//错误代码
if (WSAENOTCONN == nErrCode)
{
ShowSocketMsg("The socket is not connected!");
}else if(WSAESHUTDOWN == nErrCode)
{
ShowSocketMsg("The socket has been shut down!");
}else if (WSAETIMEDOUT == nErrCode)
{
ShowSocketMsg("The connection has been dropped!");
}else{}
return FALSE; //发送失败
}
return TRUE; //发送成功
}
/*
* 错误处理
*/
int HandleSocketError(char *str)
{
ShowSocketMsg(str); //显示错误消息
WSACleanup(); //卸载Windows socket DLL
return SERVER_API_ERROR;//退出应用程序
}
/*
* 显示错误
*/
void ShowSocketMsg(char* str)
{
MessageBox(NULL, str, "SERVER ERROR", MB_OK);
}
client:
#include "stdafx.h"
#include <windows.h>
#include <winsock.h>
#include <iostream>
#pragma comment(lib, "wsock32.lib")
using namespace std;
#define CLIENT_EXIT_OK 0 //客户端正常退出
#define CLIENT_DLL_REEOR 1 //调用Windows socket dll失败
#define CLIENT_API_ERROR 2 //调用Windows socket api失败
#define MAX_NUM_BUF 64 //缓冲区的最大长度
#define SERVERPORT 5555//服务器TCP端口
//变量
char bufRecv[MAX_NUM_BUF]; //读缓冲区
char bufSend[MAX_NUM_BUF]; //写缓冲区
SOCKET sHost; //socket
BOOL bConning; //连接服务器状态
//函数
void InitMember(void); //初始化变量
int ExitClient(int nExit); //退出
BOOL RecvLine(SOCKET s, char* buf); //读取一行数据
void ShowErrorMsg(void); //显示错误信息
//主函数
int main()
{
//初始化变量
InitMember();
WORD wVersionRequested; //应用程序需要Windows sockets DLL的版本
WSADATA wsaData; //Windows sockets DLL版本信息
int retVal; //调用Windows sockets API返回值
//初始化Windows Sockets DLL
wVersionRequested = MAKEWORD(1,1);
retVal = WSAStartup(wVersionRequested,(LPWSADATA)&wsaData);
if ( 0 != retVal )
{
MessageBox(NULL, "Can not find a usable Windows Sockets dll!", "ERROR", MB_OK);
return CLIENT_DLL_REEOR;
}
//创建Windows socket
sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == sHost)
{
ShowErrorMsg(); //显示错误信息
WSACleanup(); //释放资源
return CLIENT_API_ERROR;//退出
}
//准备连接服务器
cout << "Client succeeded!" << endl;
cout<<"Be ready to connect to server..."<<endl;
//获取主机的信息
LPHOSTENT hostEntry;
char hostname[MAX_NUM_BUF];
gethostname(hostname,MAX_NUM_BUF); //获取主机名称
hostEntry = gethostbyname(hostname); //获取主机信息
if(!hostEntry)
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}
//设置sockaddr_in
SOCKADDR_IN addrServ;
addrServ.sin_family = AF_INET;
addrServ.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
addrServ.sin_port = htons(SERVERPORT);
//连接服务器
retVal=connect(sHost,(LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
if(SOCKET_ERROR == retVal)
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}else{
bConning = TRUE; //连接服务器成功
}
//连接服务器成功
cout<<"Connect successfully!"<<endl;
//向服务器发送数据
strcpy(bufSend, "Hello, Server!\n");
retVal = send(sHost, bufSend, strlen(bufSend), 0);
if (SOCKET_ERROR == retVal)
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}
//从服务器接收数据
if (!RecvLine(sHost, bufRecv))
{
ShowErrorMsg(); //显示错误信息
return ExitClient(CLIENT_API_ERROR); //退出
}
//显示服务器的应答
cout<<bufRecv<<endl;
//退出
return ExitClient(CLIENT_EXIT_OK);
}
/*
* 显示错误信息
*/
void ShowErrorMsg(void)
{
int nErrCode = WSAGetLastError();//获取错误代码
HLOCAL hlocal = NULL;
//获取错误的文本字符串
BOOL fOk = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, nErrCode, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(PTSTR)&hlocal, 0, NULL);
//显示错误信息
if (hlocal != NULL)
{
MessageBox(NULL, (char*)LocalLock(hlocal), "CLIENT ERROR", MB_OK);
LocalFree(hlocal);
}
}
/*
* 初始化成员变量
*/
void InitMember(void)
{
//初始化读和写缓冲区
memset(bufRecv, 0, MAX_NUM_BUF);
memset(bufSend, 0, MAX_NUM_BUF);
//初始化
sHost = INVALID_SOCKET;
//没有连接状态
bConning = FALSE;
}
/*
* 退出
*/
int ExitClient(int nExit)
{
closesocket(sHost); //关闭套接字
WSACleanup(); //卸载Windows sockets DLL 清理内存
//显示退出信息
cout << "Client exiting..." << endl;
Sleep(20000);
return nExit; //退出
}
/*
* 读取一行数据
*/
BOOL RecvLine(SOCKET s, char* buf)
{
BOOL retVal = TRUE; //返回值
BOOL bLineEnd = FALSE; //行结束
int nReadLen = 0; //读入字节数
int nDataLen = 0; //数据长度
while (!bLineEnd && bConning) //与客户端连接 没有换行
{
nReadLen = recv(s, buf + nDataLen, 1, 0);//每次接收一个字节
//错误处理
if (SOCKET_ERROR == nReadLen)
{
retVal= FALSE; //读数据失败
break; //跳出循环
}
if (0 == nReadLen)//客户端关闭
{
retVal = FALSE; //读数据失败
break ; //跳出循环
}
//读入数据
if ('\n' == *(buf + nDataLen)) //换行符
{
bLineEnd = TRUE; //接收数据结束
}else{
nDataLen += nReadLen; //增加数据长度
}
}
return retVal;
}
TCP阻塞模式开发的更多相关文章
- Python socket(TCP阻塞模式)基础程式
前置知识:Python基础语法,socket库 tips: 1. 默认HOST_IP:127.0.0.1 2. 默认HOST_PORT:7676 参考代码: 1. 客户端程式 #!/usr/bin/e ...
- TCP同步与异步及阻塞模式,多线程+阻塞模式,非阻塞模式简单介绍
首先我简单介绍一下同步TCP编程 与异步TCP编程. 在服务端我们通常用一个TcpListener来监听一个IP和端口.客户端来一个请求的连接,在服务端可以用同步的方式来接收,也可以用异步的方式去接收 ...
- Socket 阻塞模式和非阻塞模式
阻塞I/O模型: 简介:进程会一直阻塞,直到数据拷贝 完成 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好. 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返 ...
- <摘录>详谈高性能TCP服务器的开发
对于开发一款高性能服务器程序,广大服务器开发人员在一直为之奋斗和努力.其中一个影响服务器的重要瓶颈就是服务器的网络处理模块.如果一款服务器程序不能及时的处理用户的数据.则服务器的上层业务逻辑再高效也是 ...
- IO通信模型(二)同步非阻塞模式NIO(NonBlocking IO)
同步非阻塞模式(NonBlocking IO) 在非阻塞模式中,发出Socket的accept()和read()操作时,如果内核中的数据还没有准备好,那么它并不会阻塞用户进程,而是立刻返回一个信息.也 ...
- IO通信模型(一)同步阻塞模式BIO(Blocking IO)
几个概念 阻塞IO 和非阻塞IO 这两个概念是程序级别的.主要描述的是程序请求操作系统IO操作后,如果IO资源没有准备好,那么程序该如何处理的问题:前者等待:后者继续执行(但是使用线程一直轮询,直到有 ...
- Linux网络编程:基于TCP的程序开发回顾篇《转》
面向连接的TCP程序设计 基于TCP的程序开发分为服务器端和客户端两部分,常见的核心步骤和流程: 其实按照上面这个流程调用系统API确实可以完全实现应用层程序的开发,一点问题没有.可随着时间的推移,你 ...
- 开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,RPC,SSL,跨平台,支持可靠UDP,支持TCP自动拆包,TCP数据报模式等
开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,RPC,SSL,跨平台,支持可靠UDP,支持TCP自动拆包,TCP数据报模式等 C++开发网络通信程序时用asio是个不错的选择 ...
- 《连载 | 物联网框架ServerSuperIO教程》- 6.并发通讯模式开发及注意事项
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
随机推荐
- magento的xml文件解读
其中显示页面的getChildHtml(’')函数是取xml文件中的block中的name属性的:
- Adaboost新理解
Adaboost有几个难点: 1.弱分类器的权重怎么理解? 误差大的弱分类器权重小,误差小的弱分类器权重大.这很好理解.在台湾大学林轩田老师的视频中,推导说,这个权值实际上貌似梯度下降,权值定义成1/ ...
- liunx的文件系统及相关的基本命令
1.ls 当前目录列表 2.cd 切换 3.cd .. 返回上一级 4.cat 文件 显示文件内容 5.mkdir 文件夹名 创建一个新的文件夹 6.rmdir 文件夹名 ...
- led不同颜色的驱动电压和驱动电流
LED耗电非常低,一般来说LED的工作电压是2-3.6V,其中红光电压是1.8-2.1V 波长610-620 绿光电压是3.0-3.5V 波长520-530 兰光电压是3.0-3.5V 波长 ...
- [LeetCode&Python] Problem 589. N-ary Tree Preorder Traversal
Given an n-ary tree, return the preorder traversal of its nodes' values. For example, given a 3-ary ...
- M端错误提醒 -- pop 使用
JS: window.pop = {/*alert提示框 *@param title 提示的标题 *@param desc 提示的描述 *@param btnNum 按钮的数量,假如为1,则无视e2, ...
- HDU 2089:不要62
Problem Description 杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer). 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来 ...
- .NET/C# 项目如何优雅地设置条件编译符号?
条件编译符号指的是 Conditional Compilation Symbols.你可以在 Visual Studio 的项目属性中设置,也可以直接在项目文件中写入 DefineConstants ...
- Omar Loves Candies
题目大意:在一个N * M的格子中,放有一些糖,这些糖有的会损害健康,有的对健康有益.有损害的被记为负数,有益的会记为正数.另外,对于每一个糖而言,他都比左边的糖和上面的糖更健康. 现在我要在在N*M ...
- ES6必知必会 (四)—— Symbol、Set和Map
Symbol 1.Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值.它是 JavaScript 语言的第七种数据类型,前六种分别是:undefined.null.布尔值(Bool ...