VC++中Socket编程的实现-TCP服务器端

预备:

加载套接字库。

过程:

1.创建套接字(socket)。
2.将套接字绑定到一个本地地址和端口上(bind)。
3.将套接字设为监听模式,准备接受客户请求(listen)。
4.等待客户请求到来;当请求到来后,接受连接请求,返回一个新对应于此次连接的套接字(accept)。
5.用返回的套接字和客户端进行通讯(send/recv)。
6.返回等待另一客户请求。
7.关闭套接字。

实现:

//加载套接字库

#include <Winsock2.h>

#include <stdio.h>

还需要添加链接库ws2_32.lib(工程=设置=链接=对象/库模块)

WORD wVersionRequested;      //准备加载Winsock库的版本,注意高字节是副版本号

WSADATA wsaData;                  //是一个返回值,指向WSADATA结构的指针,WSAStartup函数将其加载的库版本信息输入到这个结构体中。

int err;

wVersionRequested = MAKEWORD(1,1);

err = WSAStartup(wVersionRequested,&wsaData);

if(0 != err){

    return;

}

if(LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1){

    WSACleanup();                //如果加载的版本不正确,返回。需要注意的是高字节是副版本号,低字节是主版本号

    return;

}

//跟着步骤实现套接字

1.Create Socket!

socket(int af,int type,int protocol);    //socket 函数三个参数af,指定地址族,对于TCP/IP协议套接字,它只能是AF_INET;type参数指定Socket类型,对于1.1版本的Socket,它只支持两种类型的套接字,SOCK_STREAM指定产生流失套接字,SOCK_DGRAM产生数据报套接字;protocol是与特定的地址家族相关的协议,如果指定为0,那么系统就会根据地址格式和套接字类别,自动选择一个合适的协议。

SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);

2.Bind Socket to a port!

 SOCKADDR_IN addrSrv;
 addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
 addrSrv.sin_family=AF_INET;
 addrSrv.sin_port=htons(6000);

 bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

int bind(SOCKET s,const struct sockaddr FAR *name,int namelen);

//s为指定要绑定的套接字;name指定了该套接字的本地地址信息,这是一个指向sockaddr结构的指针变量,该地址结构是为所有的地址家族准备的,这个结构可能随时用的网络协议不同而不同,所以才需要第三个参数namelen指定该地址结构的长度。

sockaddr结构的定义如下:

struct sockaddr{

    u_short sa_family;

    char sa_data[14];

};

其中sa_family指定地址家族,对于TCP/IP协议的套接字,必须设置为AF_INET,sa_data[14]只是一个占位的作用,该区域中指定与协议相关的具体地址信息。由于实际要求只是内存区,所以对不同的协议家族,用不同的结构来替换sockaddr。除了sa_family外,sockadd是按网络字节顺序表示的。在基于TCP/IP的socket编程中,可以使用sockaddr_in结构替换sockaddr,以方便我们填写地址信息。

struct sockaddr_in{

    short sin_family;

    unsigned short sin_port;     //Port 2 Bytes!

    struct in_addr sin_addr;     //Address 4 Bytes!

    char sin_zero[8];                //8 Bytes!

};

sockaddr_in和sockaddr的长度是一样的。

需要注意的是sin_addr的类型是in_addr,而in_addr实际上是一个联合

struct in_addr{

    union{

        struct{u_char s_b1,s_b2,s_b3,s_b4;}S_un_b;

        struct{u_short s_w1,s_w2;}S_un_w;

        u_long S_addr;

    }S_un;

};

所以才有了上面的  addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

INADDR_ANY,表示的是所有IP!

3.将套接字设为监听模式

listen(sockSrv,5);

// int listen(SOCKET s,int backlog); s是Socket不用说,backlog是等待队列的最大长度,比如现在设为2,那么假如同时有3个请求来到服务器端的时候,就把前两个放到请求队列中,第三个就会被拒绝。

4.等待客户请求到来,返回连接套接字和通讯

 SOCKADDR_IN addrClient;
 int len=sizeof(SOCKADDR);

 while(1)  
 {
      SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);    //建立连接,返回对应于此次连接的SOCKET
      char sendBuf[100];          //发送数据的buff
      sprintf(sendBuf,"Welcome %s to my space!",
      inet_ntoa(addrClient.sin_addr));       //格式化客户端地址
      send(sockConn,sendBuf,strlen(sendBuf)+1,0);        //发送的字节多一个,最后一个是空,结束!
      char recvBuf[100];          //接收数据的buff
      recv(sockConn,recvBuf,100,0);         //接收数据
      printf("%s\n",recvBuf);
      closesocket(sockConn);                    //关闭连接
 }

//accept();这个函数是用来接受客户端发送的连接请求。并返回建立连接的Socket:

SOCKET accept(

    SOCKET s,

    struct sockaddr FAR* addr,

    int FAR* adrlen

);

s表示已经通过listen函数设为监听状态的SOCKET!addr是指向缓冲区的指针,保存发起连接的这个客户端的IP地址信息和端口信息;addrlen是addr的长度。

//send函数通过一个已经建立连接的套接字发送数据,recv是通过一个已经建立连接的套接字接收数据。两个函数的参数基本一样。

int send(SOCKET s,const char FAR* buf,int len,int flags);

不同的只有buf,对于send那是发送数据的buf,而对于recv那是接收数据的buf,flags通常情况下为0.

注意上面用了一个函数inet_ntoa是格式化IP地址的,将接受一个ULONG型的数据返回一个被格式化成了点分的字符串的IP(如:192.168.0.1),还有一个inet_addr作用和inet_ntoa正好相反。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值