C语言实现 select模式编程
来源:互联网 发布:熊族刀路软件英文原版 编辑:程序博客网 时间:2024/06/05 21:05
server:
// TcpServer.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <WINSOCK2.H> #include <iostream>#pragma comment(lib,"WS2_32.lib") #define PORT 9990 #define DATA_BUFSIZE 8192 // 定义套接字信息typedef struct _SOCKET_INFORMATION { CHAR Buffer[DATA_BUFSIZE]; // 发送和接收数据的缓冲区 WSABUF DataBuf; // 定义发送和接收数据缓冲区的结构体,包括缓冲区的长度和内容 SOCKET Socket; // 与客户端进行通信的套接字 DWORD BytesSEND; // 保存套接字发送的字节数 DWORD BytesRECV; // 保存套接字接收的字节数} SOCKET_INFORMATION, * LPSOCKET_INFORMATION; DWORD TotalSockets = 0; // 记录正在使用的套接字总数量LPSOCKET_INFORMATION SocketArray[FD_SETSIZE]; // 保存Socket信息对象的数组,FD_SETSIZE表示SELECT模型中允许的最大套接字数量// 创建SOCKET信息BOOL CreateSocketInformation(SOCKET s) { LPSOCKET_INFORMATION SI; // 用于保存套接字的信息 // printf("Accepted socket number %d\n", s); // 打开已接受的套接字编号 // 为SI分配内存空间 if ((SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION))) == NULL) { printf("GlobalAlloc() failed with error %d\n", GetLastError()); return FALSE; } // 初始化SI的值 SI->Socket = s; SI->BytesSEND = 0; SI->BytesRECV = 0; // 在SocketArray数组中增加一个新元素,用于保存SI对象 SocketArray[TotalSockets] = SI; TotalSockets++; // 增加套接字数量 return(TRUE); } // 从数组SocketArray中删除指定的LPSOCKET_INFORMATION对象void FreeSocketInformation(DWORD Index) { LPSOCKET_INFORMATION SI = SocketArray[Index]; // 获取指定索引对应的LPSOCKET_INFORMATION对象 DWORD i; // 关闭套接字 closesocket(SI->Socket); //printf("Closing socket number %d\n", SI->Socket); // 释放指定LPSOCKET_INFORMATION对象资源 GlobalFree(SI); // 将数组中index索引后面的元素前移 for (i = Index; i < TotalSockets; i++) { SocketArray[i] = SocketArray[i+1]; } TotalSockets--; // 套接字总数减1} // 主函数,启动服务器int _tmain(int argc, _TCHAR* argv[]){ SOCKET ListenSocket; // 监听套接字 SOCKET AcceptSocket; // 与客户端进行通信的套接字 SOCKADDR_IN InternetAddr; // 服务器的地址 WSADATA wsaData; // 用于初始化套接字环境 INT Ret; // WinSock API的返回值 FD_SET WriteSet; // 获取可写性的套接字集合 FD_SET ReadSet; // 获取可读性的套接字集合 DWORD Total = 0; // 处于就绪状态的套接字数量 DWORD SendBytes; // 发送的字节数 DWORD RecvBytes; // 接收的字节数 // 初始化WinSock环境 if ((Ret = WSAStartup(0x0202,&wsaData)) != 0) { printf("WSAStartup() failed with error %d\n", Ret); WSACleanup(); return -1; } // 创建用于监听的套接字 if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET) { printf("WSASocket() failed with error %d\n", WSAGetLastError()); return -1; } // 设置监听地址和端口号 InternetAddr.sin_family = AF_INET; InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); InternetAddr.sin_port = htons(PORT); // 绑定监听套接字到本地地址和端口 if(bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR) { printf("bind() failed with error %d\n", WSAGetLastError()); return -1; } // 开始监听 if (listen(ListenSocket, 5)) { printf("listen() failed with error %d\n", WSAGetLastError()); return -1; } /* // 设置为非阻塞模式 ULONG NonBlock = 1; if(ioctlsocket(ListenSocket, FIONBIO, &NonBlock) == SOCKET_ERROR) { printf("ioctlsocket() failed with error %d\n", WSAGetLastError()); return -1; } // 为ListenSocket套接字创建对应的SOCKET_INFORMATION // 这样就可以把ListenSocket添加到SocketArray数组中 */ CreateSocketInformation(ListenSocket); while(TRUE) { // 准备用于网络I/O通知的读/写套接字集合 FD_ZERO(&ReadSet); FD_ZERO(&WriteSet); // 向ReadSet集合中添加监听套接字ListenSocket FD_SET(ListenSocket, &ReadSet); // 将SocketArray数组中的所有套接字添加到WriteSet和ReadSet集合中 // SocketArray数组中保存着监听套接字和所有与客户端进行通信的套接字 // 这样就可以使用select()判断哪个套接字有接入数据或者读取/写入数据 for (DWORD i=0; i<TotalSockets; i++) { LPSOCKET_INFORMATION SocketInfo = SocketArray[i]; FD_SET(SocketInfo->Socket, &WriteSet); FD_SET(SocketInfo->Socket, &ReadSet); } // 判断读/写套接字集合中就绪的套接字 if((Total = select(0, &ReadSet, &WriteSet, NULL, NULL)) == SOCKET_ERROR) { printf("select() returned with error %d\n", WSAGetLastError()); return -1; } // 依次处理所有套接字。本服务器是一个回应服务器,即将从客户端收到的字符串再发回到客户端。 for (DWORD i=0; i<TotalSockets; i++) { LPSOCKET_INFORMATION SocketInfo = SocketArray[i]; // SocketInfo为当前要处理的套接字信息 // 判断当前套接字的可读性,即是否有接入的连接请求或者可以接收数据 if (FD_ISSET(SocketInfo->Socket, &ReadSet)) { if(SocketInfo->Socket == ListenSocket) // 对于监听套接字来说,可读表示有新的连接请求 { Total--; // 就绪的套接字减1 // 接受连接请求,得到与客户端进行通信的套接字AcceptSocket if((AcceptSocket = accept(ListenSocket, NULL, NULL)) != INVALID_SOCKET) { printf("连接成功!\n"); /* // 设置套接字AcceptSocket为非阻塞模式 // 这样服务器在调用WSASend()函数发送数据时就不会被阻塞 NonBlock = 1; if(ioctlsocket(AcceptSocket, FIONBIO, &NonBlock) == SOCKET_ERROR) { printf("ioctlsocket() failed with error %d\n", WSAGetLastError()); return -1; } */ // 创建套接字信息,初始化LPSOCKET_INFORMATION结构体数据,将AcceptSocket添加到SocketArray数组中 if(CreateSocketInformation(AcceptSocket) == FALSE) return -1; } else { if(WSAGetLastError() != WSAEWOULDBLOCK) { printf("accept() failed with error %d\n", WSAGetLastError()); return -1; } } } else // 接收数据 { // 如果当前套接字在ReadSet集合中,则表明该套接字上有可以读取的数据 if (FD_ISSET(SocketInfo->Socket, &ReadSet)) { Total--; // 减少一个处于就绪状态的套接字 memset(SocketInfo->Buffer, ' ', DATA_BUFSIZE); ZeroMemory(SocketInfo->DataBuf.buf,DATA_BUFSIZE);// 初始化缓冲区 SocketInfo->DataBuf.buf = SocketInfo->Buffer; // 初始化缓冲区位置 SocketInfo->DataBuf.len = DATA_BUFSIZE; // 初始化缓冲区长度 // 接收数据 DWORD Flags = 0; if(WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes, &Flags, NULL, NULL) == SOCKET_ERROR) { // 错误编码等于WSAEWOULDBLOCK表示暂没有数据,否则表示出现异常 if(WSAGetLastError() != WSAEWOULDBLOCK) { printf("WSARecv() failed with error %d\n", WSAGetLastError()); FreeSocketInformation(i); // 释放套接字信息 } continue; } else // 接收数据 { SocketInfo->BytesRECV = RecvBytes; // 记录接收数据的字节数 if(RecvBytes == 0) // 如果接收到0个字节,则表示对方关闭连接 { FreeSocketInformation(i); continue; } else // 如果成功接收数据,则打印收到的数据 { printf(SocketInfo->DataBuf.buf); printf("\n"); // } } } } } else { // 如果当前套接字在WriteSet集合中,则表明该套接字的内部数据缓冲区中有数据可以发送 if(FD_ISSET(SocketInfo->Socket, &WriteSet)) { Total--; // 减少一个处于就绪状态的套接字 SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->BytesSEND; // 初始化缓冲区位置 SocketInfo->DataBuf.len = SocketInfo->BytesRECV - SocketInfo->BytesSEND; // 初始化缓冲区长度 if(SocketInfo->DataBuf.len > 0) // 如果有需要发送的数据,则发送数据 { if(WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0, NULL, NULL) == SOCKET_ERROR) { // 错误编码等于WSAEWOULDBLOCK表示暂没有数据,否则表示出现异常 if(WSAGetLastError() != WSAEWOULDBLOCK) { printf("WSASend() failed with error %d\n", WSAGetLastError()); FreeSocketInformation(i); // 释放套接字信息 } continue; } else { SocketInfo->BytesSEND += SendBytes; // 记录发送数据的字节数 // 如果从客户端接收到的数据都已经发回到客户端,则将发送和接收的字节数量设置为0 if (SocketInfo->BytesSEND == SocketInfo->BytesRECV) { SocketInfo->BytesSEND = 0; SocketInfo->BytesRECV = 0; } } } } } // 如果ListenSocket未就绪,并且返回的错误不是WSAEWOULDBLOCK(该错误表示没有接收的连接请求),则出现异常 } } // 暂停,按任意键退出 system("pause"); return 0;}
client:
// TcpClient.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <Winsock2.H> #include <string>#include <iostream>#pragma comment(lib,"WS2_32.lib") #define BUF_SIZE 64 // 缓冲区大小 int _tmain(int argc, _TCHAR* argv[]){ WSADATA wsd; // 用于初始化Windows Socket SOCKET sHost; // 与服务器进行通信的套接字 SOCKADDR_IN servAddr; // 服务器地址 char buf[BUF_SIZE]; // 用于接受数据缓冲区 int retVal; // 调用各种Socket函数的返回值 // 初始化Windows Socket if(WSAStartup(MAKEWORD(2,2),&wsd) != 0) { printf("WSAStartup failed !\n"); return 1; } // 创建套接字 sHost = socket(AF_INET,SOCK_STREAM,IPPROTO_IP); if(INVALID_SOCKET == sHost) { printf("socket failed !\n"); WSACleanup(); return -1; } // 设置服务器地址 servAddr.sin_family = AF_INET; servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 用户需要根据实际情况修改 servAddr.sin_port = htons(9990); // 在实际应用中,建议将服务器的IP地址和端口号保存在配置文件中 int sServerAddlen = sizeof(servAddr); // 计算地址的长度 // 循环等待 while(true) { // 连接服务器 Sleep( 200 ); retVal = connect(sHost,(LPSOCKADDR)&servAddr,sizeof(servAddr)); Sleep( 200 ); if(SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if(err == WSAEWOULDBLOCK || err == WSAEINVAL) // 无法立即完成非阻塞套接字上的操作 { //Sleep(500); continue; } else if(err == WSAEISCONN) // 已建立连接 { break; } else { continue; //printf("connect failed !\n"); //closesocket(sHost); //WSACleanup(); //return -1; } } } // 循环向服务器发送字符串,并显示反馈信息。 // 发送quit将使服务器程序退出,同时客户端程序自身也将退出 while(true) { // 向服务器发送数据 printf("Please input a string to send: "); // 接收输入的数据 std::string str; std::getline(std::cin, str); // 将用户输入的数据复制到buf中 ZeroMemory(buf,BUF_SIZE); ZeroMemory(buf,BUF_SIZE); strcpy(buf,str.c_str()); // 循环等待 while(true) { // 向服务器发送数据 retVal = send(sHost,buf,strlen(buf),0); if(SOCKET_ERROR == retVal) { int err = WSAGetLastError(); if(err == WSAEWOULDBLOCK) // 无法立即完成非阻塞套接字上的操作 { continue; } else { printf("send failed !\n"); closesocket(sHost); WSACleanup(); return -1; } } break; } while(true) { ZeroMemory(buf,BUF_SIZE); // 清空接收数据的缓冲区 retVal = recv(sHost,buf,sizeof(buf)+1,0); // 接收服务器回传的数据 if(SOCKET_ERROR == retVal) { int err = WSAGetLastError(); // 获取错误编码 if(err == WSAEWOULDBLOCK) // 接收数据缓冲区暂无数据 { Sleep(100); printf("waiting back msg !\n"); continue; } else if(err == WSAETIMEDOUT || err == WSAENETDOWN) { printf("recv failed !\n"); closesocket(sHost); WSACleanup(); return -1; } break; } break; } //ZeroMemory(buf,BUF_SIZE); // 清空接收数据的缓冲区 //retVal = recv(sHost,buf,sizeof(buf)+1,0); // 接收服务器回传的数据 printf("Recv From Server: %s\n",buf); // 如果收到quit,则退出 if(strcmp(buf, "quit") == 0) { printf("quit!\n"); break; } } // 释放资源 closesocket(sHost); WSACleanup(); // 暂停,按任意键继续 system("pause"); return 0; }
0 0
- C语言实现 select模式编程
- 编程语言实现模式
- 编程语言实现模式
- 《编程语言实现模式》
- C语言-Socket编程之Select函数
- C语言select方式实现socket实例
- c语言编程--网络编程模式
- 适配器模式C语言实现
- 策略模式C语言实现
- 建造模式C语言实现
- command模式(c 语言实现)
- C语言实现工厂模式
- C语言分层模式实现
- C语言分层模式实现
- C语言编程实现grep
- 龙格库塔C 语言编程实现
- C语言编程实现三子棋
- Linux下C语言实现C/S模式编程(附源码,运行截图)
- 漫谈Android 增量更新
- Android使用Downloadmanager进行下载时,鉴别取消下载和下载完成的广播
- Microsoft CryptoAPI加密技术
- 算法理解的整理
- LeetCode 260. Single Number III
- C语言实现 select模式编程
- Big Data, AI for 保险。。。
- 从使用到原理学习Java线程池
- 【VS开发】【C/C++开发】传递双重指针申请内存,典型用法
- getopt和getopt_long函数
- 软件架构模式基本概念及三者区别
- iOS10系统安装新软件后无法联网的解决办法
- 存储图片
- 云服务总线CSB:“连”无边界