简单的 Winsock 应用程式设计(1)
林 军 鼐
相信各位读者现在对於 Winsock 的定义、系统环境,以及一些 Winsock Stack
及 Winsock 应用程式,都有基本的认识了。接下来笔者希望能分几期为各位读者
介绍一下简单的 Winsock 网路应用程式设计。
我们将以 Winsock 1.1 规格所定义的 46 个应用程式介面(API)为基础,逐
步来建立一对 TCP socket 主从架构(Client / Server)的程式。在这两个程式中,
Server 将使用 Winsock 提供的「非同步」(asynchronous)函式来建立 socket 连
结、关闭、及资料收送等等;而 Client 则采类似传统 UNIX 的「阻拦式」
(blocking)。由於我们的重点并不在於 MS Windows SDK 的程式设计,所以我
们将使用最简便的方式来显示讯息;有关 MS Windows 程式的技巧,请各位读者
自行研究相关的书籍及文章。
今天我们先要看一下主从架构 TCP socket 的建立连结(connect)及关闭
(close)。(参见图 1.)
(图 1. 主从架构的 TCP socket 连接建立与关闭)
以前笔者曾简单地介绍过主从架构的概念,现在我们再以生活上更浅显的例
子来说明一下,读者稍後也较容易能明白笔者的叙述。我们可以假设 Server 就像
是电信局所提供的一些服务,比如「104 查号台」或「112 障碍台」。
(1)电信局先建立好了一个电话总机,这就像是呼叫 socket() 函式开启了一
个 socket。
(2)接著电信局将这个总机的号码定为 104,就如同我们呼叫 bind() 函式,
将 Server 的这个 socket 指定(bind)在某一个 port。当然电信局必须让用户知道
这个号码;而我们的 Client 程式同样也要知道 Server 所用的 port,待会才有办法
与之连接。
(3)电信局的 104 查号台底下会有一些自动服务的分机,但是它的数量是有
限的,所以有时你会拨不通这个号码(忙线)。同样地,我们在建立一个 TCP 的
Server socket 时,也会呼叫 listen() 函式来监听等待;listen() 的第二个参数即是
waiting queue 的数目,通常数值是由 1 到 5。(事实上这两者还是有点不一
样。)
(4)用户知道了电信局的这个 104 查号服务,他就可以利用某个电话来拨号
连接这个服务了。这就是我们 Client 程式开启一个相同的 TCP socket,然後呼叫
connect() 函式去连接 Server 指定的那个 port。当然了,和电话一样,如果 waiting
queue 满了、与 Server 间线路不通、或是 Server 没提供此项服务时,你的连接就
会失败。
(5)电信局查号台的总机接受了这通查询的电话後,它会转到另一个分机做
服务,而总机本身则再回到等待的状态。Server 的 listening socket 亦是一样,当
你呼叫了 accept() 函式之後,Server 端的系统会建立一个新的 socket 来对此连接
做服务,而原先的 socket 则再回到监听等待的状态。
(6)当你查询完毕了,你就可以挂上电话,彼此间也就离线了。Client 和
Server 间的 socket 关闭亦是如此;不过这个关闭离线的动作,可由 Client 端或
Server 端任一方先关闭。有些电话查询系统不也是如此吗?
接下来,我们就来看主从架构的 TCP socket 是如何利用这些 Winsock 函式来
达成的;并利用资策会资讯技术处的「WinKing」这个 Winsock Stack 中某项功能
来显示 sockets 状态的变化。文章中仅列出程式的片段,完整的程式请看附录的程
式。
【Server 端建立 socket 并进入监听等待状态】
首先我们先看 Server 端如何建立一个 TCP socket,并使其进入监听等待的状
态。
在图 1. 上,我们可以看到最先被呼叫到的是 WSAStartup() 函式。说明如下:
WSAStartup():连结应用程式与 Winsock.DLL 的第一个函式。
格 式: int PASCAL FAR WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData );
参 数:
wVersionRequested 欲使用的 Windows Sockets API 版本
lpWSAData 指向 WSADATA 资料的指标
传回值: 成功 - 0 失败 - WSASYSNOTREADY / WSAVERNOTSUPPORTED / WSAEINVAL
说明: 此函式「必须」是应用程式呼叫到 Windows Sockets DLL 函式中的第一
个,也唯有此函式呼叫成功後,才可以再呼叫其他 Windows Sockets DLL 的函式。
此函式亦让使用者可以指定要使用的 Windows Sockets API 版本,及获取设计者的
一些资讯。
程式中我们要用 Winsock 1.1,所以我们在程式中有一段为:
WSAStartup((WORD)((1<<8)|1),(LPWSADATA) &WSAData)
其中 ((WORD)((1<<8)|1) 表示我们要用的是 Winsock 「1.1」版本,而
WSAData 则是用来储存由系统传回的一些有关此一 Winsock Stack 的资料。
再来我们呼叫 socket() 函式来开启 Server 端的 TCP socket。
socket():建立Socket。
格 式: SOCKET PASCAL FAR socket( int af, int type, int protocol );
参 数: af 目前只提供 PF_INET(AF_INET)
type Socket 的型态 (SOCK_STREAM、SOCK_DGRAM)
protocol 通讯协定(如果使用者不指定则设为0)
传回值: 成功 - Socket 的识别码
失败 - INVALID_SOCKET(呼叫 WSAGetLastError() 可得知原因)
说明: 此函式用来建立一 Socket,并为此 Socket 建立其所使用的资源。
Socket 的型态可为 Stream Socket 或 Datagram Socket.
我们要建立的是 TCP socket,所以程式中我们的第二个参数为
SOCK_STREAM,我们并将开启的这个 socket 号码记在 listen_sd 这个变数。
listen_sd = socket(PF_INET, SOCK_STREAM, 0)
接下来我们要指定一个位址及 port 给 Server 的这个 socket,这样 Client 才知
道待会要连接哪一个位址的哪个 port;所以我们呼叫 bind() 函式。
bind():指定 Socket 的 Local 位址 (Address)。
格 式: int PASCAL FAR bind( SOCKET s, const struct sockaddr FAR *name, int namelen );
参 数: s Socket的识别码
name Socket的位址值
namelen name的长度
传回值: 成功 - 0
失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明: 此一函式是指定 Local 位址及 Port 给某一未定名之 Socket。使用者若不
在意位址或 Port 的值,那麽他可以设定位址为 INADDR_ANY,及 Port 为 0;那麽
Windows Sockets 会自动将其设定适当之位址及 Port (1024 到 5000之间的值),使用
者可以在此 Socket 真正连接完成後,呼叫 getsockname() 来获知其被设定的值。
bind() 函式要指定位址及 port,这个位址必须是执行这个程式所在机器的 IP
位址,所以如果读者在设计程式时可以将位址设定为 INADDR_ANY,这样
Winsock 系统会自动将机器正确的位址填入。如果您要让程式只能在某台机器上
执行的话,那麽就将位址设定为该台机器的 IP 位址。由於此端是 Server 端,所
以我们一定要指定一个 port 号码给这个 socket。
读者必须注意一点,TCP socket 一旦选定了一个位址及 port 後,就无法再呼
叫另一次 bind 来任意更改它的位址或 port。
在程式中我们将 Server 端的 port 指定为 7016,位址则由系统来设定。
struct sockaddr_in sa;
sa.sin_family = PF_INET;
sa.sin_port = htons(7016); /* port number */
sa.sin_addr.s_addr = INADDR_ANY; /* address */
bind(listen_sd, (struct sockaddr far *)&sa, sizeof(sa))
我们在指定 port 号码时会用到 htons() 这个函式,主要是因为各机器的数值读
取方式不同(PC 与 UNIX 系统即不相同),所以我们利用这个函式来将 host
order 的排列方式转换成 network order 的排列方式;相同地,我们也可以呼叫
ntohs() 这个相对的函式将其还原。(host order 各机器不同,但 network order 都
相同)(htons 是针对 short 数值,对於 long 数值则用 hotnl 及 ntohl)
指定完位址及 port 之後,我们呼叫 listen() 函式,让这个 socket 进入监听状
态。一个 Server 端的 TCP socket 必须在做完了 listen 的呼叫後,才能接受 Client
端的连接。
listen():设定 Socket 为监听状态,准备被连接。
格 式: int PASCAL FAR listen( SOCKET s, int backlog );
参 数: s Socket 的识别码
backlog 未真正完成连接前(尚未呼叫 accept 前)彼端的连接要求的最大个数
传回值: 成功 - 0
失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明: 使用者可利用此函式来设定 Socket 进入监听状态,并设定最多可有多少
个在未真正完成连接前的彼端的连接要求。(目前最大值限制为 5, 最小值为1)
程式中我们将 backlog 设为 1 。
listen(listen_sd, 1)
呼叫完 listen 後,此时 Client 端如果来连接的话,Client 端的连接动作
(connect)会成功,不过此时 Server 端必须再呼叫 accept() 函式,才算正式完成
Server 端的连接动作。但是我们什麽时候可以知道 Client 端来连接,而适时地呼
叫 accept 呢?在这里我们就要利用一个很好用的 WSAAsyncSelect 函式,将
Server 端的这个 socket 转变成 Asynchronous 模式,让系统主动来通知我们有
Client 要连接了。(图1. 中并未将此函式绘出)
WSAAsyncSelect():要求某一 Socket 有事件 (event) 发生时通知使用者。
格 式: int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent );
参 数: s Socket 的编号
hWnd 动作完成後,接受讯息的视窗 handle
wMsg 传回视窗的讯息
lEvent 应用程式有兴趣的网路事件
传回值: 成功 - 0
失败 - SOCKET_ERRO
WinSock函数简介
需积分: 0 161 浏览量
更新于2010-10-20
收藏 36KB RAR 举报
WinSock,全称为Windows Socket,是Microsoft Windows操作系统中实现网络通信功能的一组API接口。它为应用程序提供了一种标准的方法来执行Internet协议(如TCP/IP)进行数据传输。林军鼐,作为台湾地区在IT领域的专家,可能在他的资料中详细介绍了WinSock函数的基本概念、使用方法以及实际应用。
WinSock函数是开发者构建网络应用程序的基础,主要用于创建套接字、绑定、监听、连接、接收和发送数据等任务。以下是一些关键的WinSock函数:
1. **socket()**:这是创建套接字的函数,返回一个套接字句柄,用于后续的网络通信。参数包括协议族(如AF_INET表示IPv4)、套接字类型(如SOCK_STREAM表示TCP)和协议(通常为0,系统会自动选择)。
2. **bind()**:此函数将套接字与特定的IP地址和端口号关联,确保数据包能正确发送和接收。
3. **listen()**:对于服务器端,调用listen函数设置最大等待连接队列长度,准备接收客户端的连接请求。
4. **accept()**:当有客户端请求连接时,服务器端通过调用accept函数接受连接,并返回一个新的套接字句柄,用于与该客户端的通信。
5. **connect()**:客户端调用connect函数尝试连接到服务器的指定地址和端口,建立TCP连接。
6. **send()** 和 **recv()**:这两个函数用于发送和接收数据。send负责将数据写入网络,而recv则从网络读取数据。
7. **close()**:关闭套接字,结束网络通信。
在林军鼐的讲解中,可能还涵盖了错误处理、多线程编程、异步I/O、Winsock的版本差异(如Winsock 1.1和Winsock 2.2的区别)、Winsock的性能优化策略,以及如何在实际项目中结合Winsock与其他Windows API来实现更复杂的网络功能。
例如,他可能会介绍WSAStartup和WSACleanup函数,这两个函数在使用WinSock之前和之后必须调用,以初始化和清理Winsock库。他还可能讨论了套接字选项(setsockopt和getsockopt),这些函数允许程序员调整套接字的行为。
此外,关于WinSock编程,林军鼐可能还会涉及TCP和UDP的区别,TCP保证数据的可靠传输,而UDP则提供了更快但不保证的数据传递。在实际应用中,FTP、HTTP等协议常使用TCP,而DNS查询等对实时性要求较高的场景则倾向于使用UDP。
WinSock函数是开发网络应用的核心工具,理解和掌握这些函数的用法对于任何希望在Windows平台上构建网络程序的开发者来说都至关重要。林军鼐的资料应能提供深入的见解和实践经验,帮助读者更好地理解和应用WinSock。
ycwxs
- 粉丝: 0
- 资源: 20
最新资源
- Python中的贝叶斯建模和概率编程.zip
- Python中的分布式异步超参数优化.zip
- Python中的分布式进化算法.zip
- Python中的概率时间序列建模.zip
- Python中的模糊字符串匹配.zip
- 基于matlab的信号处理,信号波形恢复,求各阶谐波,数据拟合
- springboot184基于springboot的校园网上店铺的设计与实现.zip
- springboot184基于springboot的校园网上店铺的设计与实现.zip
- Python中的回溯测试交易策略.zip
- Python中的开源低代码机器学习库.zip
- springboot187社区养老服务平台的设计与实现.zip
- springboot187社区养老服务平台的设计与实现.zip
- Python中的设计模式集合.zip
- Python中的投资组合和风险分析.zip
- springboot188基于spring boot的校园商铺管理系统.zip
- springboot188基于spring boot的校园商铺管理系统.zip