[Win32]服务器端程序

服务器端程序流程

服务器端的功能在指定的端口上监听,等待客户端的连接。在连接建立后可使用send()、recv()发送、接收数据。一般情况下,socket程序服务端过程如下

  1. 程序在运行后,首先需要调用WSAStartup()加载ws2_32.dll
  2. 调用socket()创建用于监听的SOCKET,在创建时需要指定使用的网络协议,连接类型等
  3. 调用bind()将SOCKET绑定到网络地址和端口
  4. 调用listen()开始监听
  5. 调用accept()等待客户端连接。在客户端连接后,accept()返回,得到连接SOCKET。在accept()返回后,可立即在调用,以处理其他客户端的连接
  6. 得到连接SCOKET后,可调用send()和recv()发生接收数据
  7. 在数据传输完成后,可调用closesocket()关闭SOCKET
  8. 调用WSACleanup()释放ws2_32.dll

面向连接的应用程序流程图

服务器程序

新建win32项目控制台程序 Win32Server项目:

[cpp]  view plain copy
  1. // Win32Server.cpp : 定义控制台应用程序的入口点。   
  2.   
  3. #include "stdafx.h"  
  4. #include <WinSock2.h>  
  5. #include <ws2tcpip.h>  
  6.   
  7. #pragma comment(lib,"wsock32.lib")  
  8. #pragma comment(lib,"ws2_32.lib")   
  9. #define DEFPORT "10000"  
  10. #define DEFPORTTWO  10000  
  11. //处理接收的消息  
  12. void HandleMsg(SOCKET sockfd,char* msg)  
  13. {  
  14.     int nSend=0;  
  15.     char sendBuf[2048]={0};   
  16.     if (lstrcmpiA(msg,"download") == 0)  
  17.     {         
  18.         strcpy(sendBuf,"we get downLoad\n");  
  19.     }  
  20.     else if (lstrcmpiA(msg,"get information") == 0)  
  21.     {   
  22.         strcpy(sendBuf,"we get information!!!!\n");  
  23.     }  
  24.     nSend=send(sockfd,sendBuf,strlen(sendBuf),0);  
  25.     if (nSend == SOCKET_ERROR)  
  26.     {  
  27.         printf("error at send(),threadID=%d, errno=%d\n",sockfd,WSAGetLastError());  
  28.         closesocket(sockfd);  
  29.     }  
  30.     printf("sockID=%d,send client [%d]bytes----%s",sockfd,nSend,sendBuf);   
  31. }  
  32. //连接线程  
  33. DWORD WINAPI ConnectionThread(LPVOID lpParam)  
  34. {  
  35.     DWORD dwThreadID=GetCurrentProcessId();  
  36.     SOCKET sockfd=(SOCKET)lpParam;  
  37.     int recvByte=0;  
  38.     char recvBuf[2096];  
  39.     recvByte=recv(sockfd,recvBuf,strlen(recvBuf)+1,0);  
  40.     if (recvByte == 0)//接收数据失败,连接关闭  
  41.     {  
  42.         printf("接收数据失败,连接关闭!!!");  
  43.         closesocket(sockfd);  
  44.         return 0;  
  45.     }  
  46.     else if (recvByte == SOCKET_ERROR)//接收数据失败,socket错误  
  47.     {  
  48.         printf("error at recv,erron=%d\n",WSAGetLastError());  
  49.         closesocket(sockfd);  
  50.         return 0;  
  51.     }  
  52.     else if (recvByte > 0)  
  53.     {  
  54.         printf("ConnectionThread(%d),[%d] Bytes received:%s\n",dwThreadID,recvByte,recvBuf);  
  55.         HandleMsg(sockfd,recvBuf);  
  56.     }  
  57.     closesocket(sockfd);  
  58.     return 0;  
  59. }  
  60. void main()  
  61. {  
  62.     WSADATA data;  
  63.     if(WSAStartup(MAKEWORD(2,2),&data) != NO_ERROR)  
  64.     {  
  65.         printf("error at WSAStartup,errno=%d\n",GetLastError());  
  66.         return;  
  67.     }   
  68.     SOCKET listenSocket=INVALID_SOCKET;  
  69.     if((listenSocket=socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET)  
  70.     {  
  71.         printf("error at socket(),errno=%d\n",WSAGetLastError());  
  72.         WSACleanup();  
  73.         return;  
  74.     }  
  75.     printf("socket successfully!!!\n");  
  76.        
  77. //  SOCKADDR_IN addr;  
  78. //  addr.sin_family=AF_INET;  
  79. //  addr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);  
  80. //  addr.sin_port=htons(DEFPORTTWO);  
  81. //  if (bind(listenSocket,(SOCKADDR*)&addr,sizeof(addr)) == SOCKET_ERROR)  
  82. //  {  
  83. //      printf("error at bind(),errno=%d\n",WSAGetLastError());   
  84. //      closesocket(listenSocket);  
  85. //      WSACleanup();  
  86. //      return;  
  87. //  }  
  88. //等价于下面的代码  
  89.             
  90.     addrinfo *result=NULL,hints;  
  91.     ZeroMemory(&hints,sizeof(hints));  
  92.     SOCKADDR_IN dd;  
  93.     hints.ai_family=AF_INET;  
  94.     hints.ai_socktype=SOCK_STREAM;  
  95.     hints.ai_socktype=0;  
  96.     hints.ai_flags=AI_PASSIVE;  
  97.     if(getaddrinfo(NULL,DEFPORT,&hints,&result) != 0)   
  98.     {  
  99.         printf("error at getaddrinfo\n");  
  100.         closesocket(listenSocket);  
  101.         WSACleanup();  
  102.         return;  
  103.     }  
  104.   
  105.     if (bind(listenSocket,result->ai_addr,result->ai_addrlen) == SOCKET_ERROR)  
  106.     {  
  107.         printf("error at bind(),errno=%d\n",WSAGetLastError());  
  108.         freeaddrinfo(result);  
  109.         closesocket(listenSocket);  
  110.         WSACleanup();  
  111.         return;  
  112.     }  
  113.     freeaddrinfo(result);//result不再需要  
  114.     printf("bind successfully!!!\n");  
  115.   
  116.     if (listen(listenSocket,SOMAXCONN) == SOCKET_ERROR)  
  117.     {  
  118.         printf("error at listen(),errno=%d\n",WSAGetLastError());   
  119.         closesocket(listenSocket);  
  120.         WSACleanup();  
  121.         return;  
  122.     }  
  123.     printf("listen successfully!!!\n");  
  124.   
  125.     SOCKET clientSocket=INVALID_SOCKET;  
  126.     SOCKADDR_IN clientAddr;  
  127.     int clientAddrLen=sizeof(clientAddr);  
  128.     while(1)  
  129.     {  
  130.         printf("ready to accept\n");  
  131.         clientSocket=accept(listenSocket,(SOCKADDR*)&clientAddr,&clientAddrLen);  
  132.         if(clientSocket == INVALID_SOCKET)  
  133.         {  
  134.             printf("error at accept(),errno=%d\n",WSAGetLastError());  
  135.             closesocket(listenSocket);  
  136.             break;//等待连接错误,退出循环  
  137.         }  
  138.         //printf("accept a connetion[%d.%d.%d.%d]:%d,sockfd=%d\n\n\n\n",clientAddr.sin_addr.s_net,clientAddr.sin_addr.s_host,clientAddr.sin_addr.s_lh,clientAddr.sin_addr.s_impno,ntohs(clientAddr.sin_port),clientSocket);//等价于下一行  
  139.         printf("accept a connetion[%s]:%d,sockfd=%d\n\n\n",inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port),clientSocket);  
  140.         //为每一个连接创建一个数据发送的接收线程,使服务端又可以立即接收其他的客户端连接  
  141.         if (!CreateThread(NULL,0,ConnectionThread,(LPVOID)clientSocket,0,NULL))  
  142.         {  
  143.             printf("create thread error(%d)\n",GetLastError());  
  144.             break;  
  145.         }  
  146.     }  
  147.     WSACleanup();  
  148. }  

函数解析

[cpp]  view plain copy
  1.  /* 
  2. 当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。 
  3. bind()函数把一个地址族中的特定地址赋给socket。例如,对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。 
  4. int bind(SOCKET s,sockaddr * name,int namelen); 
  5. 参 数: s:Socket对象名,它通过socket()创建了,唯一标识一个socket 
  6. name:服务器地址信息名称(包含信息有:地址协议族,服务器本机的IP,要监听的端口) 
  7. namelen:name的长度 
  8. 返回值:成功返回0,否则返回SOCKET_ERROR 
  9. 通常服务器在启动的时候会绑定一个众说周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它(ip+port)来连接服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不用调用,而是在connect()时由系统随机生成一个。 
  10.  
  11. int listen(SOCKET s,int backlog); 
  12. 参 数: s:一个已绑定的套接字 
  13. backlog:连接请求队列的最大长度(一般2~4,用SOMAXCONN则有系统确定)。socket可以排队的最大连接个数,不是最多可以连接几个客户端。更具体些:TCP模块允许的已完成三次握手过程(TCP模块完成)但还没来得及被应用程序accep()的最大连接数。 
  14. 返回值:成功返回0,否则返回SOCKET_ERROR 
  15. socket()函数创建的socket默认是一个主动类型的,listen()函数则将主动连接套接口socket变为被动连接套接口,使得这个进程可以接受其他进程的请求(客户的连接请求),从而成为一个服务器进程。 
  16.  
  17. SOCKET accept(SOCKET s,_output struct sockaddr * addr,_output int * addrlen); 
  18. 参数:s:监听套接字, 
  19. addr:存放来连接的客户端的地址和端口,(若客户端使用了bind()来绑定客户端本地的IP和Port,则服务器端会得到客户端bind的端口,而不是服务器端自动分配的端口)。。   当我们调用socket()创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。若对客户端的IP地址不感兴趣,则可以设为NULL 
  20. addrlen:addr的长度,sizeof(SOCKADDR_IN),当addr为NULL时,addrlen则可以为NULL 
  21. 返回值:功返回一个新产生的Socket对象,否则返回INVALID_SOCKET 
  22. accept()默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字就是连接套接字。 
  23. 监听套接字:监听套接字正如accept()的参数sockfd,它就是监听套接字,在调用listen()函数之后。 
  24. 连接套接字:一个套接字会从主动连接的套接字变为一个监听套接字;而accept()返回的是已连接socket描述字(一个连接套接字),它代表一个网络已经存在的点点连接。 
  25. 一个服务器通常仅仅只创建一个监听socket描述符,它在该服务器的生命周期内一直存在。内核为每个有服务器进程接收的客户端连接创建了一个已连接的socket描述符,当服务器完成了对某个客户的服务后,相应的已连接socket描述符就被关闭。 
  26. 连接套接字并没有占有新的端口与客户端通信,依然使用的是监听套接字一样的端口号。 
  27. */   
运行结果:
问题:当有多个客户端并发连接服务器端时,上程序会怎样,能处理吗?答案是不能的;需要添加一个监听线程。

此文章来自于【http://blog.csdn.net/ouyangshima/article/details/8932334】
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值