Winsock(3) 编写一个TCP服务端

7 篇文章 0 订阅
7 篇文章 0 订阅

本章介绍如何编写一个 Winsock TCP/IP服务端来接收客户连接请求
通讯分为面向连接通讯(Connection-Oriented Communication 如TCP)和非连接通讯(Connectionless Communication 如UDP)。笔记(3)至笔记(6)将介绍前者

SOCKET 是Winsock中独立的一个类型,用来表示一个连接的句柄
它的定义如下:

typedef UINT_PTR SOCKET;
typedef _W64 unsigned int UINT_PTR, *PUINT_PTR;

在Winsock中,有两种基本通讯方式:面向连接传输模式、无连接传输模式
IP协议族中,TCP/IP协议做到了面向连接通讯,TCP提供零错误数据传输机制,在发送方和接收方建立一条虚拟的连接.
服务端是一个进程,它等待若干客户连接并相应它们的请求.
服务端在一个周知地址上监听连接,在TCP/IP中,周知端口是本地接口的IP地址和端口号.
不同的协议有不同的地址组成方式因此有不同的地址命名方法.

建立一个服务端的步骤
  1. socket()/WSASocket(); //创建一个socket
  2. bind(); //绑定一个周知地址
  3. listen(); //设置监听模式
  4. accept()/WSAAccept(); //接收连接
1.socket()/WSASocket(); //创建一个socket.

有两个创建Socket的方法:socket()、WSASocket().

SOCKET socket(
    int af,
    int type,
    int protocol
);

int af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式
int type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等
int protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

Winsock提供了四个很有用的函数来控制socket的选项和行为:setsockopt()、getsockopt()、ioctlsocket()、WSAIoctl().将在后面详细介绍

2.bind(); //绑定一个周知地址
int bind(
    SOCKET s,    //服务端socket
    const struct sockaddr FAR * name,    //SOCKADDR
    int namelen    //name长度
);

使用代码如下

SOCKET s;
SOCKADDR_IN InternetAddr;
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);    //创建socket
INT nPortID=5150;
InternetAddr.sin_family=AF_INET;
InternetAddr.sin_addr.s_addr=htonl(INADDR_ANY);    //INADDR_ANY参数表示该服务端接收任意地址往该端口发送的消息
InternetAddr.sin_port=htons(nPortID);
bind(s,(SOCKADDR*)&InternetAddr,sizeof(InternetAddr));    //显式转换

错误时返回SOCKET_ERROR.
常见错误码:

  • WSAEADDRINUSE:该IP端口已被占用或者处于TIME_WAIT状态.
  • WSAEFAULT:已经绑定过后再一次绑定.
3.listen(); //设置监听模式
int listen(
SOCKET s,
int backlog    //挂起连接队列最大长度
);

设置backlog参数后仍然需要由程序底层来进行适当替换,替换为一个适合的接近值,并且用户无法得知确切的值.一般设置为5.
当挂起队列满时,连接请求会失败,返回WSAECONNREFUSED
常见错误码:

  • WSAEADDRINUSE:该IP端口已被占用或者处于TIME_WAIT状态.
  • WSAEFAULT:listen()之前会进行bind().
4.accept()/WSAAccept(); //接收连接

其实还有AcceptEx()函数,是accept()的扩展,在后面会介绍

SOCKET accept(
    socket s,    //监听状态绑定的socket
    struct sockaddr FAR * addr,    //有效的SOCKADDR_IN地址
    int FAR * addrlen    //SOCKADDR_IN的长度
);

该函数返回一个关联到请求连接的客户的socket描述符,然后使用这个socket来进行后续一系列操作.
服务端监听socket将持续存在来监听其他客户的请求.
发生错误时,返回INVALID_SOCKET错误.
常见错误码:

  • WSAEWOULDBLOCK:监听socket在异步或非阻塞模式并且无连接可以接受.
一个完整的 Winsock TCP/IP 服务端代码如下
int main(void)
{
WSADATA wsaData;
SOCKET ListeningSocket;
SOCKET NewConnection;
SOCKADDR_IN ServerAddr;
SOCKADDR_IN ClientAddr;
int port=5150;
int ClientAddrLen;

WSAStartup(MAKEWORD(2,2),&wsaData);    //初始化 Winsock 2.2 版本
ListeningSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);    //创建一个 socket 来监听客户连接
ServerAddr.sin_family=AF_INET;    //填充 SOCKADDR_IN 数据结构
ServerAddr.sin_port=htons(port);
ServerAddr.sin_addr.s_addr=htonl(ADDR_ANY);
bind(ListeningSocket,(SOCKADDR*)&ServerAddr,sizeof(ServerAddr));    //绑定一个周知地址
listen(ListeningSocket,5);    //设置监听模式
ClientAddrLesizeof(ClientAddr);    //显示指定ClientAddrLen大小
NewConnection=accept(ListeningSocket,(SOCKADDR*)&ClientAddr,&ClientAddrLen);
//接受一个到来的连接,注意!最后一个参数需要自己显式指定!
//这里你通过这些socket可以做两件事
//1.通过ListeningSocket再次调用accept()来接受其他连接
//2.通过NewConnection来发送/接受数据
//当你做完这两件事情时必须要关闭这些socket
//socket的关闭将在后面介绍
closesocket(ListeningSocket);    //关闭ListeningSocket
closesocket(NewConnection);    //关闭NewConnection
WSACleanup();    //关闭Winsock
return 0;
}

注意 accept()函数的最后一个参数需要显式指定大小!
ClientAddrLesizeof(ClientAddr); //显式指定ClientAddrLen大小
好了,到目前为止你已经可以编写一个 Winsock TCP/IP服务端来接收客户连接请求了.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值