Linux下select应用

来源:互联网 发布:零基础学java看什么书 编辑:程序博客网 时间:2024/06/09 14:47
 为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort);uint32_t ntohl(uint32_t netlong);uint16_t ntohs(uint16_t netshort);    这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。  FD_CLR(s,*set):从集合set中删除描述字s。    FD_ISSET(s,*set):若s为集合中一员,非零;否则为零。    FD_SET(s,*set):向集合添加描述字s。    FD_ZERO(*set):将set初始化为空集NULL。 sockaddr结构体sockaddr的缺陷:sa_data把目标地址和端口信息混在一起了struct sockaddr {       unsigned short sa_family;   char sa_data[14];                     }; sa_family是通信类型,最常用的值是 "AF_INET"sa_data14字节,包含套接字中的目标地址和端口信息   sockaddr_in 结构体sockaddr_in结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中struct sockaddr_in {    short int sin_family;   unsigned short int sin_port;      struct in_addr sin_addr;struct in_addr {     unsigned long s_addr;           }     unsigned char sin_zero[8];}      sin_port和sin_addr都必须是NBO一般可视化的数字都是HBO(本机字节顺序)    sin_zero 初始值应该使用函数 bzero() 来全部置零。   一般采用下面语句struct sockaddr_in cliaddr;bzero(&cliaddr,sizeof(cliaddr));   sockaddr_in结构体变量的基本配置struct sockaddr_in ina;bzero(&ina,sizeof(ina));ina.sin_family=AF_INET;ina.sin_port=htons(23);ina.sin_addr.s_addr = inet_addr("132.241.5.10");      sockaddr 和 sockaddr_in的相互关系一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数    sockaddr_in用于socket定义和赋值    sockaddr用于函数参数   最典型的源、目的节点socket定义对于源、目的地址和源、目的地址端口,需要建立两个socket变量cliaddr绑定源地址和源端口servaddr用于connect和sendto的设定目的地址和目的端口struct sockaddr_in servaddr,cliaddr;create_socket(char *server_addr_string,unsigned int server_port){源socket赋值       bzero(&cliaddr,sizeof(cliaddr));       cliaddr.sin_family = AF_INET;       通常TCP/UDP 协议源地址和端口都是随机的       cliaddr.sin_addr.s_addr = htons(INADDR_ANY);       cliaddr.sin_port = htons(0);目的socket赋值       bzero(&servaddr,sizeof(servaddr));       servaddr.sin_family = AF_INET;       inet_aton(server_addr_string,&servaddr.sin_addr);       servaddr.sin_port = htons(server_port);}    网络字节顺序 (Network Byte Order)      NBO结构体的sin_port和sin_addr都必须是NBO   本机字节顺序 (Host Byte Order)    HBO一般可视化的数字都是HBO    NBO,HBO二者转换inet_addr()    将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)inet_aton()    将字符串点数格式地址转化成NBOinet_ntoa ()     将NBO地址转化成字符串点数格式htons()    "Host to Network Short"htonl()    "Host to Network Long"ntohs()    "Network to Host Short"ntohl()    "Network to Host Long"常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型    三种给socket赋值地址的方法inet_aton(server_addr_string,&myaddr.sin_addr);myaddr.sin_addr.s_addr = inet_addr("132.241.5.10");INADDR_ANY转不转NBO随便myaddr.sin_addr.s_addr = htons(INADDR_ANY);  myaddr.sin_addr.s_addr = INADDR_ANY;    两种给socket 赋值端口的方法#define MYPORT 3490 myaddr.sin_port = htons(MYPORT);0(随机端口)转不转NBO随便myaddr.sin_port = htons(0);myaddr.sin_port = 0;      htons/l和ntohs/l等数字转换都不能用于地址转换,因为地址都是点数格式,所以地址只能采用数字/字符串转换如inet_aton,inet_ntoa;唯一可以用于地址转换的htons是针对INADDR_ANY cliaddr.sin_addr.s_addr = htons(INADDR_ANY)   inet_addr()与inet_aton()的区别    inet_addr()    是返回值型struct sockaddr_in ina;ina.sin_addr.s_addr = inet_addr("132.241.5.10");     inet_aton()     是参数指针型struct sockaddr_in ina;inet_aton("132.241.5.10",&ina.sin_addr);   inet_ntoa  将NBO地址转化成字符串点数格式参数:结构体变量.sinaddr返回值:字符串指针a1 = inet_ntoa(ina.sin_addr);printf("address 1: %s\n",a1); address 1: 132.241.5.10     inet_addr()的缺陷:必须对-1做检测处理因为inet_addr()的结果是整型,而发生错误时返回-1。而 ina.sin_addr.s_addr是unsigned long型-1long short显示成111111111,和IP地址255.255.255.255相符合!会被误认为广播地址!Windows Socket 异步编程(非阻塞模式) -- Select回送示例2012-09-06 19:26 3455人阅读 评论(0) 收藏 举报socketwindows编程服务器nulliostream使用Select异步模式来实现返送示例。服务器启动并监听9999端口,并将收到的客户端信息打印并返送给客户端。重点理解的是:一个套接字是否是可读、可写状态。当服务器端socket在Accept成功之后,便是可读状态,接收客户端发送数据。当客户端发送recv函数时,这个socket便成为可写状态,服务器端便知道这个客户端可写,然后根据自己的定义发送给客户端内容。如果客户端不发送recv函数,即下面Client中的recv函数的话,服务器端保存的客户端这个socket便没有进入可写状态的时候,也就不会有回送的情况发生。ServerServer复制代码  1 #include <WINSOCK2.H>     2 #include <iostream>  3   4 #pragma comment(lib,"WS2_32.lib")     5   6 using namespace std;  7   8 #define   PORT   9999    9 #define   DATA_BUFSIZE   8192  10  11  12 // 定义套接字信息 13 typedef   struct   _SOCKET_INFORMATION   {    14     CHAR   Buffer[DATA_BUFSIZE];        // 发送和接收数据的缓冲区 15     WSABUF   DataBuf;                        // 定义发送和接收数据缓冲区的结构体,包括缓冲区的长度和内容 16     SOCKET   Socket;                            // 与客户端进行通信的套接字 17     DWORD   BytesSEND;                    // 保存套接字发送的字节数 18     DWORD   BytesRECV;                    // 保存套接字接收的字节数 19 } SOCKET_INFORMATION,   *   LPSOCKET_INFORMATION;    20  21 DWORD   TotalSockets = 0;                // 记录正在使用的套接字总数量 22 LPSOCKET_INFORMATION   SocketArray[FD_SETSIZE];            // 保存Socket信息对象的数组,FD_SETSIZE表示SELECT模型中允许的最大套接字数量 23  24 // 创建SOCKET信息 25 BOOL   CreateSocketInformation(SOCKET   s)    26 {    27     LPSOCKET_INFORMATION   SI;                                        // 用于保存套接字的信息        28 //   printf("Accepted   socket   number   %d\n",   s);            // 打开已接受的套接字编号 29 // 为SI分配内存空间 30     if ((SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR, sizeof(SOCKET_INFORMATION)))   ==   NULL)    31     {    32         printf("GlobalAlloc()   failed   with   error   %d\n",   GetLastError());    33         return   FALSE;    34     }    35     // 初始化SI的值     36     SI->Socket = s;    37     SI->BytesSEND = 0;    38     SI->BytesRECV = 0;    39      40     // 在SocketArray数组中增加一个新元素,用于保存SI对象  41     SocketArray[TotalSockets]   =   SI;        42     TotalSockets++;                        // 增加套接字数量 43  44     return(TRUE);    45 }      46  47 // 从数组SocketArray中删除指定的LPSOCKET_INFORMATION对象 48 void   FreeSocketInformation(DWORD   Index)    49 {    50     LPSOCKET_INFORMATION SI = SocketArray[Index];    // 获取指定索引对应的LPSOCKET_INFORMATION对象 51     DWORD   i;    52      53     closesocket(SI->Socket);       // 关闭套接字 54     GlobalFree(SI);   // 释放指定LPSOCKET_INFORMATION对象资源 55 // 将数组中index索引后面的元素前移 56     if (Index != (TotalSockets-1)) 57     { 58         for (i = Index; i < TotalSockets; i++)    59         {    60             SocketArray[i] = SocketArray[i+1];    61         }  62     } 63    64     TotalSockets--;        // 套接字总数减1 65 }    66  67  68 int main() 69 { 70     SOCKET   ListenSocket;                    // 监听套接字 71     SOCKET   AcceptSocket;                    // 与客户端进行通信的套接字 72     SOCKADDR_IN   InternetAddr;            // 服务器的地址 73     WSADATA   wsaData;                        // 用于初始化套接字环境 74     INT   Ret;                                            // WinSock API的返回值 75     FD_SET   WriteSet;                            // 获取可写性的套接字集合 76     FD_SET   ReadSet;                            // 获取可读性的套接字集合 77     DWORD   Total = 0;                                // 处于就绪状态的套接字数量 78     DWORD   SendBytes;                        // 发送的字节数 79     DWORD   RecvBytes;                        // 接收的字节数 80  81  82 // 初始化WinSock环境 83     if ((Ret = WSAStartup(MAKEWORD(2,2),&wsaData)) != 0)    84     {    85         printf("WSAStartup()   failed   with   error   %d\n",   Ret);    86         WSACleanup();    87         return -1;    88     }    89     // 创建用于监听的套接字  90     if ((ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED))   ==   INVALID_SOCKET)      91     {    92         printf("WSASocket()   failed   with   error   %d\n",   WSAGetLastError());    93         return -1;    94     }    95     // 设置监听地址和端口号 96     InternetAddr.sin_family = AF_INET;    97     InternetAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);    98     InternetAddr.sin_port = htons(PORT);    99     // 绑定监听套接字到本地地址和端口100     if(bind(ListenSocket, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)   101     {   102         printf("bind()   failed   with   error   %d\n",   WSAGetLastError());   103         return -1;   104     }   105     // 开始监听106     if   (listen(ListenSocket,   5))   107     {   108         printf("listen()   failed   with   error   %d\n",   WSAGetLastError());   109         return -1;   110     }   111     // 设置为非阻塞模式112     ULONG NonBlock = 1;   113     if(ioctlsocket(ListenSocket, FIONBIO, &NonBlock) == SOCKET_ERROR)   114     {   115         printf("ioctlsocket() failed with error %d\n", WSAGetLastError());   116         return -1;   117     }   118     119     CreateSocketInformation(ListenSocket);// 为ListenSocket套接字创建对应的SOCKET_INFORMATION,把ListenSocket添加到SocketArray数组中120     121     while(TRUE)   122     {   123         FD_ZERO(&ReadSet);// 准备用于网络I/O通知的读/写套接字集合124         FD_ZERO(&WriteSet);   125         126         FD_SET(ListenSocket,   &ReadSet);// 向ReadSet集合中添加监听套接字ListenSocket127 // 将SocketArray数组中的所有套接字添加到WriteSet和ReadSet集合中,SocketArray数组中保存着监听套接字和所有与客户端进行通信的套接字128 // 这样就可以使用select()判断哪个套接字有接入数据或者读取/写入数据129         for   (DWORD i=0; i<TotalSockets; i++)   130         {131             LPSOCKET_INFORMATION SocketInfo = SocketArray[i];132             FD_SET(SocketInfo->Socket,   &ReadSet);//这说明该socket有读操作。而读操作是客户端发起的133             FD_SET(SocketInfo->Socket,   &WriteSet);//这说明该socket有写操作。134             135         }136         // 判断读/写套接字集合中就绪的套接字    137         if((Total = select(0, &ReadSet, &WriteSet, NULL, NULL)) == SOCKET_ERROR)//将NULL以形参传入Timeout,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止.服务器会停到这里等待客户端相应138         {   139             printf("select()   returned   with   error   %d\n",   WSAGetLastError());   140             return -1;   141         }      142         // 依次处理所有套接字。本服务器是一个回应服务器,即将从客户端收到的字符串再发回到客户端。143         for   (DWORD i=0; i<TotalSockets; i++)   144         {145             LPSOCKET_INFORMATION SocketInfo = SocketArray[i];            // SocketInfo为当前要处理的套接字信息146 // 判断当前套接字的可读性,即是否有接入的连接请求或者可以接收数据147             if (FD_ISSET(SocketInfo->Socket,   &ReadSet))   148             {149                 if(SocketInfo->Socket == ListenSocket)        // 对于监听套接字来说,可读表示有新的连接请求150                 {151                     Total--;    // 就绪的套接字减1152 // 接受连接请求,得到与客户端进行通信的套接字AcceptSocket153                     if((AcceptSocket = accept(ListenSocket, NULL, NULL))   !=   INVALID_SOCKET)   154                     {   155                         // 设置套接字AcceptSocket为非阻塞模式156 // 这样服务器在调用WSASend()函数发送数据时就不会被阻塞157                         NonBlock   =   1;   158                         if(ioctlsocket(AcceptSocket, FIONBIO, &NonBlock)   ==   SOCKET_ERROR)   159                         {   160                             printf("ioctlsocket()   failed   with   error   %d\n",   WSAGetLastError());   161                             return -1;                             162                         }   163                         // 创建套接字信息,初始化LPSOCKET_INFORMATION结构体数据,将AcceptSocket添加到SocketArray数组中164                         if(CreateSocketInformation(AcceptSocket) == FALSE)   165                             return -1;   166                     }   167                     else   168                     {   169                         if(WSAGetLastError() != WSAEWOULDBLOCK)   170                         {   171                             printf("accept()   failed   with   error   %d\n",   WSAGetLastError());   172                             return -1;   173                         }   174                     }                   175                 }176                 else   // 接收数据177                 {178                     Total--;                // 减少一个处于就绪状态的套接字179                     memset(SocketInfo->Buffer, ' ', DATA_BUFSIZE);            // 初始化缓冲区180                     SocketInfo->DataBuf.buf = SocketInfo->Buffer;            // 初始化缓冲区位置181                     SocketInfo->DataBuf.len   =   DATA_BUFSIZE;                // 初始化缓冲区长度182 // 接收数据183                     DWORD  Flags = 0;   184                     if(WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes, &Flags,NULL, NULL) == SOCKET_ERROR)   185                     {   186                         // 错误编码等于WSAEWOULDBLOCK表示暂没有数据,否则表示出现异常187                         if(WSAGetLastError() != WSAEWOULDBLOCK)        188                         {   189                             printf("WSARecv()   failed   with   error   %d\n",   WSAGetLastError());   190                             FreeSocketInformation(i);        // 释放套接字信息191                         }   192                         continue;   193                     }     194                     else   // 接收数据195                     {   196                         SocketInfo->BytesRECV = RecvBytes;        // 记录接收数据的字节数197                         SocketInfo->DataBuf.buf[RecvBytes] = '\0';198                         if(RecvBytes == 0)                                    // 如果接收到0个字节,则表示对方关闭连接199                         {   200                             FreeSocketInformation(i);   201                             continue;   202                         }   203                         else204                         {205                             cout << SocketInfo->DataBuf.buf << endl;// 如果成功接收数据,则打印收到的数据206                         }207                     }208                 }   209             }210             else211             {212                 // 如果当前套接字在WriteSet集合中,则表明该套接字的内部数据缓冲区中有数据可以发送213                 if(FD_ISSET(SocketInfo->Socket,  &WriteSet))   214                 {   215                     Total--;            // 减少一个处于就绪状态的套接字216                     SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->BytesSEND;            // 初始化缓冲区位置217                     SocketInfo->DataBuf.len = SocketInfo->BytesRECV - SocketInfo->BytesSEND;    // 初始化缓冲区长度218                     if(SocketInfo->DataBuf.len > 0)        // 如果有需要发送的数据,则发送数据219                     {220                         if(WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0, NULL, NULL) == SOCKET_ERROR)   221                         {   222                             // 错误编码等于WSAEWOULDBLOCK表示暂没有数据,否则表示出现异常223                             if(WSAGetLastError() != WSAEWOULDBLOCK)   224                             {   225                                 printf("WSASend()   failed   with   error   %d\n", WSAGetLastError());   226                                 FreeSocketInformation(i);        // 释放套接字信息227                             }   228                             continue;   229                         }   230                         else   231                         {   232                             SocketInfo->BytesSEND += SendBytes;            // 记录发送数据的字节数233 // 如果从客户端接收到的数据都已经发回到客户端,则将发送和接收的字节数量设置为0234                             if (SocketInfo->BytesSEND   ==   SocketInfo->BytesRECV)   235                             {   236                                 SocketInfo->BytesSEND   =   0;   237                                 SocketInfo->BytesRECV   =   0;   238                             }   239                         }   240                     }241                 }               242 243             }    // 如果ListenSocket未就绪,并且返回的错误不是WSAEWOULDBLOCK(该错误表示没有接收的连接请求),则出现异常244 245         }            246     }   247     system("pause");248     return 0;249 }复制代码ClientClient复制代码 1 #include <Winsock.h> 2 #include <string> 3 #include <iostream> 4  5 #pragma comment(lib, "ws2_32.lib") 6 using namespace std; 7  8  9 #define BUFSIZE 6410 #define PORT 999911 12 int main()13 {14     WSAData wsaData;15     SOCKET sHost;16     sockaddr_in addrServ;17     char buf[BUFSIZE];18     int retVal;19 20     if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)21     {22         cout << "WSAStartup失败!" << endl;23         return -1;24     }25 26     sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);27     if (INVALID_SOCKET == sHost)28     {29         cout << "socket() 错误!" << endl;30         WSACleanup();31         return -1;32     }33 34     addrServ.sin_family = AF_INET;35     addrServ.sin_port = htons(PORT);36     addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");37 38     retVal = connect(sHost, (LPSOCKADDR)&addrServ, sizeof(addrServ));39     if (SOCKET_ERROR == retVal)40     {41         cout << "connect 错误!" << endl;42         closesocket(sHost);43         WSACleanup();44         return -1;45     }46 47     while (true)48     {49         cout << "输入要发给服务器的内容" << endl;50         //         string msg;51 //         getline(cin, msg);52         char msg[BUFSIZE];53         cin.getline(msg, BUFSIZE);54         ZeroMemory(buf, BUFSIZE);55         strcpy(buf, msg);56         retVal = send(sHost, buf, strlen(buf), 0);57         if (SOCKET_ERROR == retVal)58         {59             cout << "发送失败" << endl;60             closesocket(sHost);61             WSACleanup();62             return -1;63         }64 65         retVal = recv(sHost, buf, sizeof(buf)+1, 0);66         cout << "从服务器端接收:" << buf << endl;67         if (strcmp(buf, "quit") == 0)68         {69             cout << "quit" << endl;70             break;71         }72     }73 74     closesocket(sHost);75     WSACleanup();76 77 78     return 0;
0 0
原创粉丝点击