Winsock编程原理

本文深入解析Windows Socket编程中的面向连接与无连接系统调用,包括服务器端与客户端的实现流程,通过具体代码实例展示了如何创建、绑定、监听、接受连接、发送与接收数据以及关闭套接字的完整过程。

面向连接的系统调用

面向无连接的系统调用

 

1.Winsock的打开
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
成功返回0,失败返回不同的错误信息。

2.服务器建立套接字
SOCKET socket(int af, int type, int protocol);
成功返回套接字对象,失败返回INVALID_SOCKET
af:说明套接口要使用的协议地址族,目前只提供AF_INET表示互联网(IP)协议。
type:描述套接字的类型,只能是SOCK_STREAM,SOCK_DGRAM,SOCK_RAW 3个协议中的一个,分别表示流套接字,数据报套接字和原始套接字
protocol:该套接字使用的特定通信协定(如果使用者不指定则为0)

3.服务器绑定端口
int bind(SOCKET s, const sockaddr FAR* name, int namelen);
成功返回0,失败返回SOCKET_ERROR
s:表示未绑定的套接字的对象名,他是socket()函数调用成功时返回的值。
name:套接字的地址值,是一个与指定协议有关的地址结构指针,这个地址必须是执行这个程式所在的机器的IP地址,
在winsock中使用sockaddr_in结构指定IP地址和端口信息,定义如下:
struct sockaddr_in
{
 short sin_family;
 u_short sin_port;
 struct in_addr sin_addr;
 char sin_zero[8];
};
其中sin_family必须为AF_INET,指明使用的是IP地址族;sin_port和sin_addr分别是以网络字节顺序表示的16位端口号和32位IP地址,
sin_zero字段一般为0;
namelen:地址参数name的长度。

4.客户端提出连接申请
int PASCAL FAR connection(SOCKET s, const struct sockaddr FAR* name, int namelen);
s:socket的识别码
name:socket想要连接的对方地址
namelen:name的长度监听
当服务端的套接字对象绑定完成之后,服务器端需要一个监听的队列来接收客户端的连接请求.listen()函数是服务器端的套接字进入监听状态。
int listen(SOCKET s, int backlog);
成功返回0,失败返回SOCKET_ERROR
s:表明一个已经绑定了的地址,需要建立监听的套接字
backlog:指定正在等待连接的最大连接个数。
服务器端调用accept()函数完成建立连接,为了知道什么时候客户端提出连接要求,从而服务器端的套接口在恰当的时候调用accept()函数完成连接的建立,需要使用
WSAAsyncSelect()函数,让系统主动通知有客户端提出连接请求了。
int  WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);
s:套接字对象
hWnd:接收消息的窗口句柄
wMsg:传给窗口的消息
IEvent:被注册的网络时间,也即是应用程序向窗口发送消息的网路事件。该值为下列值FD_READ,FD_WRITE,FD_OOB,FD_CONNECT,FD_CLOSE的组合
可以再窗口处理自定义消息函数中使用以下结构来响应Socket的不同事件:
switch(lParam)
{
 case FD_READ:
  ...
  bread;
 case FD_WRITE:
  ...
  break;
 ...
}

5.服务器端接受客户端的连接请求
SOCKET accept(SCOKET s, struct sockaddr FAR* addr, int FAR* addrlen);
调用成功,返回一个新的套接字对象与客户端的套接字相通。失败返回INVALID_SOCKET.
s:Socket的识别码
addr:存放来连接的客户端的地址
addrlen:addr的长度

6.数据的传送
int send(SOCKET s, const char FAR* buf, int len, int flags);
成功返回发送资料的长度,失败返回SOCKET_ERROR
s:socket的识别码
buf:存放要传送的资料的暂存区
len:buf的长度
flags:此函数被调用的方式
int recv(SOCKET s, char FAR* buf, int len, int flags);
成功返回接收资料的长度,失败返回SOCKET_ERROR
s:socket的识别码
buf:存放要接收的资料的暂存区
len:buf的长度
flags:此函数被调用的方式输入缓存内有效的资料,但其数量不超过len的大小

7.关闭套接字
int closesocket(SOCKET s);

8.关闭Winsock
int WSACleanup(void); //对应每个WSAStartup()函数,必须有一个WSACleanup()函数的调用

实例代码:

1)服务器端代码

View Code
 1 #include <WinSock2.h>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #pragma comment(lib,"ws2_32.lib")
 5 using namespace std;
 6 
 7 #define PORT 5000
 8 int main()
 9 {
10     int port = PORT;
11     WSADATA wsaData;
12     if (WSAStartup(MAKEWORD(2,2),&wsaData) != 0)
13     {
14         perror("winsock load failed!\n");
15         return 1;
16     }
17     //创建套接字
18     SOCKET sListen = socket(AF_INET, SOCK_STREAM, 0);
19     if (sListen == INVALID_SOCKET)
20     {
21         perror("create socket faild! \n");
22         return 1;
23     }
24     //建立服务器地址
25     struct sockaddr_in serv;
26     struct sockaddr_in client;
27     serv.sin_family = AF_INET;    
28     //把一个双字节主机字节顺序的数据转换为网络字节顺序
29     serv.sin_port = htons(port);
30     //把四字节主机字节顺序转换为网络字节顺序,INADDR_ANY为系统指定的IP地址
31     serv.sin_addr.s_addr = htonl(INADDR_ANY);
32     if (bind(sListen, (LPSOCKADDR)&serv, sizeof(serv)) == SOCKET_ERROR)
33     {
34         perror("bind socket failed ! \n");
35         return 1;
36     }
37     //进入监听状态
38     if (listen(sListen,5) == SOCKET_ERROR)
39     {
40         perror("listen socket failed! \n");
41         return 1;
42     }
43     //进入循环等待客户端连接
44     int iLen = sizeof(client);
45     while(1)
46     {
47         SOCKET sAccept = accept(sListen, (LPSOCKADDR)&client, &iLen);
48         if (sAccept == INVALID_SOCKET)
49         {
50             perror("accept socket failed! /n");
51             break;
52         }
53         printf("accepted client IP:[%s],port:[%d]\n", inet_ntoa(client.sin_addr),ntohs(client.sin_port));
54 
55         //给连接的客户发消息
56         char buf[] = "hello client,i am server!";
57         int iSend = send(sAccept,buf,sizeof(buf),0);
58         if (iSend == SOCKET_ERROR)
59         {
60             perror("send buf failed \n");
61             break;
62         }
63         else if (iSend == 0)
64         {
65             break;
66         }
67         else
68             printf("send()byte:%d \n",iSend);
69         printf("********************\n");
70         char recv_buf[1024] = {0};
71         int recv_len = recv(sAccept,recv_buf,sizeof(recv_buf),0);
72         if (recv_len == SOCKET_ERROR)
73         {
74             perror("server recv failed \n");
75             return 1;
76         }
77         else{
78             printf("recv from client:%s \n",recv_buf);
79         }
80     }
81 
82 
83     closesocket(sListen);
84     WSACleanup();
85     //return 0;
86 }

2)客户端代码

View Code
 1 #include <WinSock2.h>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #pragma comment (lib,"ws2_32.lib")
 5 using namespace std;
 6 
 7 #define PORT 5000
 8 #define BUFFER 1024
 9 int main(int argc,  char* argv[])
10 {
11     //初始化socket
12     WSADATA wsaData;
13     if(0 != WSAStartup(MAKEWORD(2,2),&wsaData))
14     {
15         perror("winsock load failed \n");
16         return 1;
17     }
18     //建立客户端流套接字
19     SOCKET client = socket(AF_INET,SOCK_STREAM,0);
20     if (client == INVALID_SOCKET)
21     {
22         perror("create socket failed\n");
23         return 1;
24     }
25     //连接服务端
26     struct sockaddr_in serv;
27     serv.sin_family = AF_INET;    //需要连接的服务器地址信息
28     int port = PORT;
29     serv.sin_port = htons(port);
30     serv.sin_addr.s_addr = inet_addr(argv[1]);    //将命令行的IP地址转化为二进制表示的网络字节书序IP地址
31     if (connect(client,(const struct sockaddr*)&serv,sizeof(serv)) == INVALID_SOCKET)
32     {
33         perror("connect faild\n");
34         return 1;
35     }
36     else
37     {
38         char buf[BUFFER] = {0};
39         int iLen = recv(client,buf,sizeof(buf),0);
40         if (iLen == SOCKET_ERROR)
41         {
42             perror("recv data failed \n");
43             return 1;
44         }
45         printf("recv() data from server:%s\n",buf);
46         char send_buf[] = "hello server, i am client.";
47         int send_len = send(client,send_buf,sizeof(send_buf),0);
48         if (send_len == SOCKET_ERROR)
49         {
50             perror("client send failed \n");
51             return 1;
52         }
53         else
54             printf("send()bytes: %d\n",send_len);
55     }
56     closesocket(client);
57     WSACleanup();
58     printf("please wait..........");
59     while(1);
60 
61 }

 

转载于:https://www.cnblogs.com/kkkwar/archive/2012/05/02/2479401.html

第一章 简介 1.1 什么是Windows Sockets规范? Windows Sockets规范以U.C. Berkeley大学BSD UNIX中流行的Socket接口为范例定义了一套Micosoft Windows下网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数;也包含了一组针对Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。 Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。此外,在一个特定版本Windows的基础上,Windows Sockets也定义了一个二进制接口(ABI),以此来保证应用Windows Sockets API的应用程序能够在任何网络软件供应商的符合Windows Sockets协议的实现上工作。因此这份规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。 遵守这套Windows Sockets规范的网络软件,我们称之为Windows Sockets兼容的,而Windows Sockets兼容实现的提供者,我们称之为Windows Sockets提供者。一个网络软件供应商必须百分之百地实现Windows Sockets规范才能做到现Windows Sockets兼容。 任何能够与Windows Sockets兼容实现协同工作的应用程序就被认为是具有Windows Sockets接口。我们称这种应用程序为Windows Sockets应用程序。 Windows Sockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCP/IP)连接,尤其要指出的是所有的Windows Sockets实现都支持流套接口和数据报套接口. 应用程序调用Windows Sockets的API实现相互之间的通讯。Windows Sockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通讯工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值