Winsock 编程流程

最近看了《Window程序设计》感觉在网络方面讲的不错,讲的很通俗易懂,与大家一同交流

转载请注明出处:http://blog.csdn.net/u010484477谢谢^_^

使用 Winsock 编程的一般步骤是比较固定的。

1.Winsock 库的装入、初始化和释放

所有的 WinSock 函数都是从 WS2_32.DLL 库导出的,VC++在默认情况下并没有连接到该库,如果想使用 Winsock API,就必须包含相应的库文件。

#pragma commment(lib, "wsock32.lib")

WSAstartup 必须是应用程序首先调用的 Winsock 函数。它允许应用程序指定所需的Windows Sockets API 的版本,获取特定 Winsock 实现的详细信息。仅当这个函数成功执行之后,应用程序才能调用其他 Winsock API。

int WSAStartup(

WORD wVersionRequested, // 应用程序支持的最高WinSock库版本。高字节为次版本号,低字节为主版本号

LPWSADATA lpWSAData // 一个指向WSADATA结构的指针。它用来返回DLL 库的详细信息

);

lpWSAData 参数用来取得 DLL 库的详细信息,结构定义如下。

typedef struct WSAData {

WORD wVersion;
// 库文件建议应用程序使用的版本

WORD wHighVersion;// 库文件支持的最高版本

char szDescription[WSADESCRIPTION_LEN+1]; // 库描述字符串

char szSystemStatus[WSASYS_STATUS_LEN+1]; // 系统状态字符串

unsigned short iMaxSockets;// 同时支持的最大套节字的数量

unsigned short iMaxUdpDg;// 2.0 版中已废弃的参数

char FAR * lpVendorInfo;
// 2.0 版中已废弃的参数

} WSADATA, FAR * LPWSADATA;

函数调用成功返回 0。否则要调用 WSAGetLastError 函数查看出错的原因。此函数的作用相当于 Win32 API GetLastError,它取得最后发生错误的代码。

每一个对WSAStartup的调用必须对应一个对WSACleanup的调用,这个函数释放Winsock库。

int  WSACleanup(void);

2.套节字的创建和关闭

使用套节字之前,必须调用 socket 函数创建一个套节字对象,此函数调用成功将返回套节字句柄

SOCKET socket(

int af,// 用来指定套节示使用的地址格式,WinSock中只支持AF_INET

int type,// 用来指定套节字的类型

int protocol// 配合type 参数使用,用来指定使用的协议类型。可以是IPPROTO_TCP等

);

type 参数用来指定套节字的类型。套节字有流套节字、数据报套节字和原始套节字等,

下面是常见的几种套节字类型定义:

SOCK_STREAM 流套节字,使用 TCP 协议提供有连接的可靠的传输

SOCK_DGRAM 数据报套节字,使用 UDP 协议提供无连接的不可靠的传输

SOCK_RAW 原始套节字,WinSock 接口并不使用某种特定的协议去封装它,而是有程序自行处理数据报以及协议首部

当 type 参数指定为 SOCK_STREAM 和 SOCK_DGRAM 时,系统已经明确确定使用 TCP和 UDP 协议来工作,所以 protocol 参数可以指定为 0。

函数执行失败返回 INVALID_SOCKET(即-1),可以通过调用 WSAGetLastError 取得错误代码。

当不使用 socket 创建的套节字时,应该调用 closesocket 函数将它关闭。如果没有错误发生,函数返回 0,否则返回 SOCKET_ERROR。

closesocket函数用法如下。

int closesocket(SOCKET s); // 函数惟一的参数就是要关闭的套节字的句柄

3.绑定套节字到指定的 IP 地址和端口号

为套节字关联本地地址的函数是 bind,用法如下。

int bind(

SOCKET s,// 套节字句柄

const struct sockaddr* name,// 要关联的本地地址

int namelen// 地址的长度

);

bind 函数用在没有建立连接的套节字上,它的作用是绑定面向连接的或者无连接的套节字。当一个套节字被 socket 函数创建以后,他存在于指定的地址家族里,但是它是未命名的。bind 函数通过安排一个本地名称到未命名的 socket 建立此 socket 的本地关联。

本地名称包含 3个部分:主机地址、协议号(分别为 UDP 或 TCP)和端口号。

// 填充sockaddr_in结构

sockaddr_in sin;

sin.sin_family = AF_INET;

sin.sin_port = htons(8888);

sin.sin_addr.S_un.S_addr = INADDR_ANY;

// 绑定这个套节字到一个本地地址

if(::bind(s, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)

{

printf("Failed bind() \n");

::WSACleanup();

return 0;

}

sockaddr_in 结构中的 sin_familly 字段用来指定地址家族,该字段和 socket 函数中的 af 参数的含义相同,所以惟一可以使用的值就是 AF_INET。

sin_port 字段和 sin_addr 字段分别指定套节字需要绑定的端口号
IP 地址。放入这两个字段的数据的字节顺序必须是网络字节顺序

由于网络字节顺序和 Intel CPU 的字节顺序刚好相反,所以必须首先用 htons 函数进行转换。如果应用程序不关心所使用的地址,可以为互联网地址指定 INADDR_ANY,为端口号指定 0。如果互联网地址等于 INADDR_ANY,系统会自动使用当前主机配置的所有 IP 地址,这简化了程序设计;如果端口号等于 0,程序执行时系统会分配一个惟一的端口号到这个应用程序,其值在 1024 到 5000 之间。应用程序可以在 bind 之后使用 getsockname 来知道为它分配的地址。但是要注意,直到套节字连接上之后
getsockname 才可能填写互联网地址,因为对一个主机来说可能有多个地址是可用的。

4.设置套节字进入监听状态

listen 函数置套节字进入监听状态。

int listen(

SOCKET s,// 套节字句柄

int backlog// 监听队列中允许保持的尚未处理的最大连接数量

);

为了接受连接,首先使用 socket 函数创建一个套节字,然后使用
bind 函数绑定它到一个本地地址,再用
listen 函数为到达的连接指定一个 backlog,最后使用 accept 接受请求的连接。listen 仅应用在支持连接的套节字上,如 SOCK_STREAM 类型。函数成功执行之后,套节字 s 进入了被动模式,到来的连接会被通知,排队等候接受处理。在同一时间处理多个连接请求的服务器通常使用 listen 函数:如果一个连接请求到达,并且排队已满,客户端将接接收 WSAECONNREFUSED 错误。

5.接受连接请求

accept 函数用于接受到来的连接。

SOCKET accept(

SOCKET s,// 套节字句柄

struct sockaddr* addr,// 一个指向sockaddr_in结构的指针,用于取得对方的地址信息

int* addrlen// 是一个指向地址长度的指针

);

该函数在 s 上取出未处理连接中的第一个连接,然后为这个连接创建一个新的套节字,返回它的句柄。新创建的套节字是处理实际连接的套节字,它与 s 有相同的属性。

程序默认工作在阻塞模式下,这种方式下如果没有未处理的连接存在,accept 函数会一直等待下去直到有新的连接发生才返回。

addrlen 参数用于指定 addr 所指空间的大小,也用于返回返回地址实际长度。如果 addr或者 addrlen 是 NULL,则没有关于远程地址的信息返回。客户端程序在创建套节字之后,要使用 connect 函数请求与服务器连接,函数原型如下。

int connect(

SOCKET s,// 套节字句柄

const struct sockaddr FAR * name, // 一个指向 sockaddr_in 结构的指针,包含了要连接的服务器的地址信息。

int namelen// sockaddr_in结构的长度

);

第一个参数 s 是此连接使用的客户端套节字。另两个参数 name 和 namelen 用来寻址远程套节字(正在监听的服务器套节字)。

6.收发数据

对流套节字来说,一般使用 send 和 recv 函数来收发数据。

int send(

SOCKET s,// 套节字句柄

const char FAR * buf,// 要发送数据的缓冲区地址

int len,// 缓冲区长度

int flags// 指定了调用方式,通常设位0

);

int recv( SOCKET s, char FAR * buf, int len, int );

send 函数在一个连接的套节字上发送缓冲区内的数据,返回发送数据的实际字节数。

recv函数从对方接收数据,并存储指定的缓冲区。flags 参数在这两函数中通常设为 0。

在阻塞模式下,send 将会阻塞线程的执行直到所有的数据发送完毕(或者一个错误发生),

recv 函数将返回尽可能多的当前可用信息,一直到缓冲区指定的大小。

下图为服务器程序和客户程序的创建过程:


最后是送大家一句我很喜欢的话:

你必须用自己的努力换取成功,然后成功就会像一个大巴掌,打在那些曾经看不起你的人脸上

Winsock 编程流程,布布扣,bubuko.com

时间: 2024-12-28 04:53:46

Winsock 编程流程的相关文章

shell脚本编程——流程控制

shell脚本编程--流程控制 目   录 一. if 二. case 三. for 四. while 五. until 六. 综合应用 一.if 1.语法 (1)单分支 if  判断条件:then fi (2)双分支 if 判断条件; then 条件为真的分支代码 else 条件为假的分支代码 fi (3)多分支 if 判断条件1; then 条件为真的分支代码 elif 判断条件2; then 条件为真的分支代码 elif 判断条件3; then 条件为真的分支代码 else 以上条件都为假

基于UDP的Winsock编程(C++版)

基于UDP的Winsock编程与基于TCP的Winsock编程相比,只是缺少了一个步骤而已.对于Server,缺少了接受连接的过程(accept()函数调用):对于Client,缺少了请求连接的过程(connect()函数调用). 另外与TCP区别的还有,在UDP中,数据收发函数是:sendto(),和recvfrom()函数. 函数原型为: int sendto(SOCKET s,const char FAR *buf,int len,int flags,const struct sockad

Java TCP/UDP socket 编程流程总结

最近正好学习了一点用java socket编程的东西.感觉整体的流程虽然不是很繁琐,但是也值得好好总结一下. Socket Socket可以说是一种针对网络的抽象,应用通过它可以来针对网络读写数据.就像通过一个文件的file handler就可以都写数据到存储设备上一样.根据TCP协议和UDP协议的不同,在网络编程方面就有面向两个协议的不同socket,一个是面向字节流的一个是面向报文的. 对socket的本身组成倒是比较好理解.既然是应用通过socket通信,肯定就有一个服务器端和一个客户端.

NetBIOS与Winsock编程接口

最近在看网络编程方面的书,由于不是通信专业出身的,以前理解的网络体系感觉就是tcp/ip,最近工作上接触到了一些光环网等乱七八糟的东西,有些基本的LC.SC连接器都不认识.花时间看了下计算机网络体系结构的知识,从ARPRANET开始计算机网络的实现方式可以分为资源子网和通信子网.如下图: 资源子网主要由网络的服务器.工作站.共享的打印机和其他设备及相关软件所组成.通信子网:是指网络中实现网络通信功能的设备及其软件的集合,包括原始的信号中继器.集线器,网桥.交换机.路由器网关等设备.ARPNET出

winsock编程WSAEventSelect模型

winsock编程WSAEventSelect模型 WSAEventSelect模型和WSAAsyncSelec模型类似,都是用调用WSAXXXXXSelec函数将socket和事件关联并注册到系统,并将socket设置成非阻塞模式.二者不同之处在于socket事件的通知方法:WSAAsyncSelec模型利用窗口句柄和消息映射函数通知网络事件,而WSAEventSelect模型利用WSAEVENT通知网络事件.完成WSAEventSelect模型需要涉及以下函数或结构: 1:WSAEventS

winsock编程IOCP模型实现代码

winsock编程IOCP模型实现代码 话不多说,上代码.借鉴<windows核心编程>部分源码和CSDN小猪部分代码. stdafx.h依赖头文件: 1 #include <iostream> 2 #include <WinSock2.h> 3 #include <MSWSock.h> 4 #include <vector> 5 #include "Singleton.h" 6 #include "IOCPWrap

2.1 LibCurl编程流程(转)

转载地址:http://blog.chinaunix.net/u/17660/showart_1822514.html2 LibCurl编程2.1 LibCurl编程流程在基于LibCurl的程序里,主要采用callback function (回 调函数)的形式完成传输任务,用户在启动传输前设置好各类参数和回调函数,当满足条件时libcurl将调用用户的回调函数实现特定功能.下面是利用libcurl完成传输任务的流程:1. 调用curl_global_init()初始化libcurl2. 调用

winsock编程学习笔记

以下部分转自博客http://blog.csdn.net/phunxm/article/details/5085869 套接字地址(sockaddr.sockaddr_in) 1 /* 2 * Structure used by kernel to store most addresses. 3 */ 4 struct sockaddr { 5 u_short sa_family;/* address family,AF_X */ 6 char sa_data[14];/* up to 14 b

10、shell编程+流程控制+分支嵌套

SHELL 编程 shell 是一个命令解释器,侦听用户指令.启动这些指令.将结果返回给用户(交互式的shell) shell 也是一种简单的程序设计语言.利用它可以编写一些系统脚本. 查看本机shell 信息: cat /etc/shells     --查看本支持的shell echo $SHELL --查看当前支持的shell shell 在执行命令时,各种命令的优先级: 别名 -> 关键字 -> 函数 -> 内置命令 -> 脚本 别名:命令的另一种形式,有些情况下可以简化命