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 函数将返回尽可能多的当前可用信息,一直到缓冲区指定的大小。

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


 

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

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

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Winsock是Windows Socket的缩写,是Windows操作系统提供的一组API,可以在Windows平台上实现网络编程。下面是一个简单的Winsock编程实现聊天程序的示例代码: 客户端: ```c++ #include <winsock2.h> #include <iostream> #include <string> #pragma comment(lib, "ws2_32.lib") using namespace std; int main() { WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != NO_ERROR) { cout << "WSAStartup failed: " << iResult << endl; return 1; } SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket == INVALID_SOCKET) { cout << "Error at socket(): " << WSAGetLastError() << endl; WSACleanup(); return 1; } sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr("127.0.0.1"); clientService.sin_port = htons(27015); iResult = connect(clientSocket, (SOCKADDR*)&clientService, sizeof(clientService)); if (iResult == SOCKET_ERROR) { cout << "Failed to connect: " << WSAGetLastError() << endl; WSACleanup(); return 1; } // 发送消息 string message; while (getline(cin, message)) { iResult = send(clientSocket, message.c_str(), message.length(), 0); if (iResult == SOCKET_ERROR) { cout << "send failed: " << WSAGetLastError() << endl; closesocket(clientSocket); WSACleanup(); return 1; } } iResult = shutdown(clientSocket, SD_SEND); if (iResult == SOCKET_ERROR) { cout << "shutdown failed: " << WSAGetLastError() << endl; closesocket(clientSocket); WSACleanup(); return 1; } closesocket(clientSocket); WSACleanup(); return 0; } ``` 服务端: ```c++ #include <winsock2.h> #include <iostream> #include <string> #pragma comment(lib, "ws2_32.lib") using namespace std; int main() { WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != NO_ERROR) { cout << "WSAStartup failed: " << iResult << endl; return 1; } SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (listenSocket == INVALID_SOCKET) { cout << "Error at socket(): " << WSAGetLastError() << endl; WSACleanup(); return 1; } sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = INADDR_ANY; service.sin_port = htons(27015); iResult = bind(listenSocket, (SOCKADDR*)&service, sizeof(service)); if (iResult == SOCKET_ERROR) { cout << "bind failed: " << WSAGetLastError() << endl; closesocket(listenSocket); WSACleanup(); return 1; } iResult = listen(listenSocket, SOMAXCONN); if (iResult == SOCKET_ERROR) { cout << "listen failed: " << WSAGetLastError() << endl; closesocket(listenSocket); WSACleanup(); return 1; } SOCKET clientSocket = accept(listenSocket, NULL, NULL); if (clientSocket == INVALID_SOCKET) { cout << "accept failed: " << WSAGetLastError() << endl; closesocket(listenSocket); WSACleanup(); return 1; } char recvbuf[512]; int recvbuflen = 512; do { iResult = recv(clientSocket, recvbuf, recvbuflen, 0); if (iResult > 0) { recvbuf[iResult] = '\0'; cout << "Received message: " << recvbuf << endl; } else if (iResult == 0) { cout << "Connection closing..." << endl; } else { cout << "recv failed: " << WSAGetLastError() << endl; closesocket(clientSocket); WSACleanup(); return 1; } } while (iResult > 0); iResult = shutdown(clientSocket, SD_SEND); if (iResult == SOCKET_ERROR) { cout << "shutdown failed: " << WSAGetLastError() << endl; closesocket(clientSocket); WSACleanup(); return 1; } closesocket(clientSocket); WSACleanup(); return 0; } ``` 这个程序只能实现客户端与服务端之间的单向通信,如果想实现双向通信,需要在服务端代码中加入发送消息的代码。 ### 回答2: WinSock编程是一种用于实现网络通信的编程技术,可以实现聊天程序。在聊天程序的实现中,需要使用两个主要的Socket函数:socket()和bind()。 首先,我们需要创建一个被动的套接字,用于接收客户端的连接。这可以通过调用socket()函数来实现。然后,我们使用bind()函数将创建的套接字绑定到一个特定的端口上,以便客户端能够与之建立连接。 接下来,我们需要使用listen()函数将套接字置于监听状态,以便等待客户端的连接请求。一旦有客户端连接进来,我们可以使用accept()函数接受连接,并为该客户端创建一个新的套接字。 然后,我们可以使用recv()和send()函数来实现服务器和客户端之间的消息交换。服务器使用recv()函数接收客户端发送的消息,而客户端使用send()函数向服务器发送消息。这样,就可以实现基本的聊天功能。 在编写程序时,需要注意处理异常情况和错误。例如,当客户端断开连接时,服务器应该能够处理该情况,并清理资源。使用try-catch语句可以捕获异常并进行适当的处理。 除了基本的聊天功能,我们还可以通过添加其他功能来提升聊天程序的体验,例如多人聊天、文件传输等。这可以通过在程序中添加额外的功能和相应的Socket函数来实现。 总之,通过使用WinSock编程,我们可以实现一个简单的聊天程序。这个程序利用Socket函数来实现服务器和客户端之间的网络通信,并通过recv()和send()函数实现消息的传输。编写聊天程序时需要注意处理异常情况和错误,以提供稳定可靠的用户体验。 ### 回答3: WinSock是一种用于在Windows操作系统上进行网络编程API。通过使用WinSock编程,我们可以实现一个简单的聊天程序。 首先,我们需要在程序中包含WinSock的头文件和链接。然后,我们需要创建一个套接字,它将作为客户端或服务器与其他计算机通信的端点。 在客户端程序中,我们首先需要使用WSAStartup函数初始化WinSock。然后,我们可以创建一个套接字,并使用connect函数将其连接到服务器的IP地址和端口。接下来,我们可以使用send函数向服务器发送消息,使用recv函数接收服务器发送的消息。最后,我们使用closesocket函数关闭套接字。 在服务器程序中,我们首先也需要使用WSAStartup函数初始化WinSock。然后,我们可以创建一个套接字,并使用bind函数将其绑定到一个IP地址和端口。接下来,我们使用listen函数开始监听客户端连接请求。当客户端连接到服务器时,我们使用accept函数接受连接,并创建一个新的套接字来与该客户端通信。然后,我们可以使用send函数向客户端发送消息,使用recv函数接收客户端发送的消息。最后,我们使用closesocket函数关闭套接字。 通过以上步骤,我们可以实现一个简单的聊天程序。当客户端和服务器之间建立连接后,它们可以相互发送消息,实现实时通信。当然,为了更好的用户体验,我们还可以在程序中添加一些额外的功能,如界面设计、消息记录等。 总而言之,通过WinSock编程,我们可以轻松实现一个基于客户端-服务器模型的聊天程序。该程序可以让多个计算机相互通信,为用户提供便捷的沟通方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值