使用C++标准库编写网络程序

 
使用 C++ 标准库编写网络程序
 
整理: Ackarlix
 
MFC 类库为我们提供了 方便、好用 CAsyncSocket CSocket ,但是 MFC 本身就是一个迷宫,进去一不小心就出不来了。像 CAsyncSocket CSocket 它们是实现是很复杂的,里面实现异步消息是通过窗体的消息机制来实现的,常常出现初始化时的错误,即使运行一段时间程序也常常出现莫名其妙的错误导致程序崩溃。你不要以为是 MFC 的问题, MFC 是绝对不会承认的,有时候你不得不采用逐行注释代码的方法来确定什么地方导致程序的错误,这个过程是相当痛苦的,最后你还得为错误买单,肯定是你编程的错误,但你又不知道错在哪儿。
我有一次编写录音功能的 网络 程序,接收到远程录音传输的数据保存在内存中,后来发现程序不定时的在启动的时候调用 CSocket.Create 出错。程序都了 N 遍也不知道为什么,关键是不定时的,调试也麻烦,最后只好采用注释的方式。最终发现接收到录音数据保存到内容那段代码注释掉就好了,我认真看了那段代码,绝对没有内存溢出会导致覆盖掉 CSocket ,每次接收数据总和也不会超过 1M ,不可能把内容耗尽 。其实很多很成熟的软件都有类似的错误,老版本的迅雷也曾经出现过这种错误,错误的提示我忘了。
用了一年的 CAsyncSocket 后,我下了决心以后决不用它了,当然也不会用从它继承而来的 CSocket 。那就用 C++ SOCKET ,什么操作和错误都是掌握在自己的手中。没有了 CAsyncSocket 的异步消息机制自己用线程来做, 网络 编程多线程是第一课。
经过长期的实贱,封装了如下 SOCKET 函数,其实使用也挺简单的:
 
1 。客户端函数:
#include<stdlib.h>
#include<winsock.h>
#include<stdio.h>
#include<string.h>
 
#ifndef INADDR_NONE
#define    INADDR_NONE 0xffffffff
#endif
 
//******************************************************
//
// 本函数负责与服务平台联系
//
//******************************************************
 
SOCKET SocketConnect(const char *host,const char *service,const char *transport)
{
       struct protoent *protoin;// 传输协议信息
       struct sockaddr_in ipaddr;// 主机的 IP 地址信息
       struct hostent *hostin;// 主机的信息
       struct servent *servin;// 服务器(主机)信息
       int sock,type;// 套接字描述符
 
       // ipaddr 结构快速清零
       memset(&ipaddr,0,sizeof(ipaddr));
       // 地址结构
       ipaddr.sin_family=AF_INET;
 
       // 从服务器类型得到端口号
       if(servin=getservbyname(service,transport))
              ipaddr.sin_port=servin->s_port;// 端口号
       else
              if((ipaddr.sin_port=htons((u_short)atoi(service)))==0)
              {
                     printf("get server information error/n");
                     WSACleanup();
                     return INVALID_SOCKET;
              }
              // 由传输协议得到对应的传输协议编码
              if((protoin=getprotobyname(transport))==0)
              {
                     printf("get protocol information error/n");
                     WSACleanup();
                     return INVALID_SOCKET;
              }
              // 从主机名获取主机 IP
              if(hostin=gethostbyname(host))
                     memcpy(&ipaddr.sin_addr,hostin->h_addr,hostin->h_length);
              else
                     if((ipaddr.sin_addr.s_addr=inet_addr(host))==INADDR_NONE)
                     {
                            printf("get host IP infomation error/n");
                            WSACleanup();
                            return INVALID_SOCKET;
                     }
                     // 根据传输类型给变量赋值
                     if(strcmp(transport,"udp")==0)
                            type=SOCK_DGRAM;
                     else
                            type=SOCK_STREAM;
                     // 创建套接字描述符
                     sock=socket(PF_INET,type,protoin->p_proto);
                     if(sock==INVALID_SOCKET)
                     {
                            printf("creat socket error/n");
                            WSACleanup();
                            return INVALID_SOCKET;
                     }
 
                     // 连接指定 IP 地址机器的指定服务端口,如果连接失败,则退出
                     if(connect(sock,(struct sockaddr*)&ipaddr,sizeof(ipaddr))==SOCKET_ERROR)
                     {
                            printf("connect socket error!please start server first/n");
                            WSACleanup();
                            return INVALID_SOCKET;
                     }
                     return sock;
}
 
//************************************
//UDP 传输类型
//************************************
SOCKET UDPConnect(const char *host,const char *service)
{
       return SocketConnect(host,service,"udp");
}
 
//************************************
//TCP 传输类型
//************************************
SOCKET TCPConnect(const char *host,const char *service)
{
       return SocketConnect(host,service,"tcp");
}
 
2 。客户端函数调用例子 (TCP)
// 启动程序后即连接服务器,连接成功后接收控制台输入,发送到服务端
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<winsock.h>
#pragma comment(lib,"wsock32")
 
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
 
SOCKET TCPConnect(const char*,const char*);
void TCPecho(char *,char*);
 
#define LINELEN 128
#define WAVERS MAKEWORD(2,0)//WORD MAKEWORD(byte bLow,byte bHigh);
 
void main(int argc,char *argv[])
{
       char *host="localhost";//IP 地址
       char *service="3333";// 默认端口号
       WSADATA wsadata;
       switch(argc)
       {
              case 1:
                     host="localhost";// 使用默认 IP
                     break;
              case 2:
                     host=argv[1];// 使用命令行给的 IP
                     break;
              case 3:
                     host=argv[1];
                     service=argv[2];// 使用命令行 IP 和端口号
                     break;
              default:
                     printf("argment error/n");
                     exit(1);
       }
      
       if(WSAStartup(WAVERS,&wsadata)!=0)// 初始化 Winsock
       {
              printf("initalize failed/n");
           WSACleanup();// 清除 Winsock
           exit(1);
       }
      
       TCPecho(host,service);
      
       WSACleanup();
      
       exit(0);
}
 
void TCPecho(char *host,char *service)
{
       char buf[LINELEN+1];
       SOCKET s;
       int outchars;
 
       s=TCPConnect(host,service);// 采用 TCP 协议连接服务程序
       while(fgets(buf,sizeof(buf),stdin))// 循环调用得到用户输入 , 当输入的数据为回车时退出
       {
              buf[LINELEN]='/0';
              outchars=strlen(buf);
              send(s,buf,outchars,0);// 发送消息
 
              if(buf[0]=='/n')
                     break;
       }
       closesocket(s);
}
 
3 。服务端函数:
#include<string.h>
#include<winsock.h>
#include<stdlib.h>
#include<stdio.h>
//
//SocketServer
//
SOCKET SocketServer(const char *service,const char*transport,int qlen)
{
       struct sockaddr_in ipaddr;// 主机 IP 地址
       struct servent *servin;// 主机信息
       struct protoent *protoin;// 传输类型
       int sock,type;// 套接字描述符
 
       memset(&ipaddr,0,sizeof(ipaddr));
       ipaddr.sin_family=AF_INET;
       ipaddr.sin_addr.s_addr=INADDR_ANY;
 
       // 从服务器名得到服务端口
       if(servin=getservbyname(service,transport))
              ipaddr.sin_port=htons(ntohs((u_short)servin->s_port));
       else
              if((ipaddr.sin_port=htons((u_short)atoi(service)))==0)
              {
                     printf("get portnumber error/n");
                     WSACleanup();
                     return INVALID_SOCKET;
              }
              // 从传输协议得到对应的编号
              if((protoin=getprotobyname(transport))==0)
              {
                     printf("get protocol number error/n");
                     WSACleanup();
                     return INVALID_SOCKET;
              }
              // 根据传输协议给对应的变量赋值
              if(strcmp(transport,"udp")==0)
                     type=SOCK_DGRAM;
              else
                     type=SOCK_STREAM;
              // 创建套接字
              sock=socket(PF_INET,type,protoin->p_proto);
              if(sock==INVALID_SOCKET)
              {
                     printf("creat socket error/n");
                     WSACleanup();
                     return INVALID_SOCKET;
              }
              // 绑定本地 IP
              if(bind(sock,(struct sockaddr*)&ipaddr,sizeof(ipaddr))==SOCKET_ERROR)
              {
                     printf("socket bind error/n");
                     WSACleanup();
                     return SOCKET_ERROR;
              }
              // 如果是流式传输( TCP )使套接字处于监听状态,等待来自客户机的连接,参数 qlen 之指定等待
              // 队列长度
              if(type==SOCK_STREAM)
              {
                     if(listen(sock,qlen)==SOCKET_ERROR)
                     {
                            printf("socket listen errror/n");
                            WSACleanup();
                            return SOCKET_ERROR;
                     }
              }
              return sock;
}
 
 
//************************************
//UDPServer
//************************************
SOCKET UDPServer(const char *service)
{
       return SocketServer(service,"udp",0);
}
 
//************************************
//TCPServer
//************************************
SOCKET TCPServer(const char *service,int qlen)
{
       return SocketServer(service,"tcp",qlen);
}
 
4 。服务器例子 (TCP)
// 接受客户端连接,每接收到客户端发送而来的数据则打印在控制台
#include<winsock.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#pragma comment(lib,"wsock32")
 
#define QLEN 5
#define BUFSIZE 5
#define WAVERS MAKEWORD(2,0)//WORD MAKEWORD(byte bLow,byte bHigh);
 
SOCKET TCPServer(const char*service,int qlen);
void main(int argc,char*argv[])
{
       char *service="3333";// 默认端口号
       struct sockaddr_in fsin;
       SOCKET msock,ssock;
       WSADATA wsadata;
       int alen,cc;
       char buf[BUFSIZE];
 
       switch(argc)
       {
       case 1:
              break;// 采用默认端口号
       case 2:
              service=argv[1];// 采用命令行端口号
              break;
       default:
              printf("argment error/n");
              exit(1);
       }
       if(WSAStartup(WAVERS,&wsadata)!=0)// 初始化 Winsock
       {
              printf("initalize failed/n");
              WSACleanup();// 清除 Winsock
              exit(1);
       }
 
       msock=TCPServer(service,QLEN);// 采用 TCP 协议
 
       while(1)
       {
              alen=sizeof(struct sockaddr);
              ssock=accept(msock,(struct sockaddr*)&fsin,&alen);//accept 阻塞接收客户端请求
 
              if(ssock==INVALID_SOCKET)
              {
                     printf("initialize failed/n");
                     WSACleanup();
                     exit(1);
              }
              while(cc=recv(ssock,buf,sizeof(buf)-1,0))// 接收客户端信息,当收到的信息为零时,则退出
              {
                     buf[cc]='/0';
                     printf("%s",buf);
              }
              printf("connect close...");
              closesocket(ssock);
              break;
       }
}
 
以上是两个完整的客户端和服务端程序,可建立两个 VC 控制台工程编译运行,进行连接测试。
 
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值