socket 接口已普遍存在于古代操作系统中
- Windows 下的 socket 编程接口与 Linux 中简直雷同
-
不同之处
- 返回类型不同(句柄类型)
- 句柄不是文件描述符,Window 中并不是所有接文件 (因而 windows 下对于 socket 无奈应用 send、recv)
Windows 下 socket() 的用法
SOCKET s = {0};
s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // IPPROTO_TCP 明确指明创立 TCP 协定的套接字
if (s == INVALID) { // 创立套接字时出错,返回 INVALID_SOCKET
ERROR("...");
return -1;
}
Windows 网络编程接口
#include <winsock2.h>
函数原型 | 性能形容 |
SOCKET socket(int af, int type, int protocal); | 创立套接字,为网络连接做筹备 |
int connect(SOCKET s, const struct sockaddr *addr, int len); | 连贯指定地址的近程设施 |
int send(SOCKET s, const char *buf, int len, int flags); | 发送数据到近程设施 |
int recv(SOCKET s char *buf, int len, int flags); | 接管近程设施发回的数据 |
int closesocket(SOCKET s); | 敞开连贯,销毁套接字 |
int bind(SOCKET s, const struct sockaddr *addr, int len); | 将套接字与指定地址进行关联 |
int listen(SOCKET s, int backlog); | 将套接字推入监听状态,期待连贯 |
SOCKET accept(SOCKET s, struct sockaddr *addr, int len); | 接管客户端连贯 |
int shutdown(SOCKET s, int howto); | 敞开连贯,进行发送和接管 |
closes 与 shutdown(Linux 下也存在)的区别:shoutdown 不开释 socket 资源
几点细微差别
- 通过 WSAStartup() 初始化零碎环境(最先调用)
- socket(), accept() 谬误返回 INVALID_SOCKET(不可默认为 -1)
- bind(), listen() 谬误返回 SOCKET_ERROR (不可默认为 -1)
- connect(), send(),recv() 谬误返回 SOCKET_ERROR (不可默认为 -1)
- 通过 WSACleanup() 革除零碎环境(最初调用)
Windows 网络编程的非凡阐明
- 在工程属性中设置链接 ws2_32.lib
- 定义变量 WSADATA wd
-
抉择 socket 版本并初始化 WSAStartup(MAKEWORD(2, 2), &wd)
-
Windows 中存在多个 socket 版本
- MAKEWORD(1, 2) // 主版本为 1,副版本为 2,返回 0x0201
- MAKEWORD(2, 2) // 主版本为 2,副版本为 2,返回 0x0202
-
编程试验:Windows 网络编程示例
client.c
#include "stdafx.h"
#include <winsock2.h>
int _tmain(int argc, _TCHAR* argv[])
{
SOCKET sock = 0; // 留神 socket 类型
struct sockaddr_in addr = {0};
int len = 0;
char buf[128] = {0};
char input[32] = {0};
int r = 0;
WSADATA wd = {0}; // 留神变量定义
if(WSAStartup(MAKEWORD(2, 2), &wd) != 0 ) // 留神初始化零碎环境
{printf("startup error\n");
return -1;
}
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == INVALID_SOCKET) // 留神返回值类型
{printf("socket error\n");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(8888);
if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR ) // 留神返回值类型
{printf("connect error\n");
return -1;
}
printf("connect success\n");
while(1)
{printf("Input:");
scanf("%s", input);
len = send(sock, input, strlen(input) + 1, 0);
r = recv(sock, buf, sizeof(buf), 0);
if(r > 0)
{printf("Receive: %s\n", buf);
}
else
{break;}
}
closesocket(sock); // 留神
WSACleanup(); // 留神革除零碎环境
return 0;
}
server.c
// Server.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <winsock2.h>
int _tmain(int argc, _TCHAR* argv[])
{
SOCKET server = 0;
struct sockaddr_in saddr = {0};
SOCKET client = 0;
struct sockaddr_in caddr = {0};
int asize = 0;
int len = 0;
char buf[32] = {0};
int r = 0;
WSADATA wd = {0};
if(WSAStartup(MAKEWORD(2, 2), &wd) != 0 )
{printf("startup error\n");
return -1;
}
server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(server == INVALID_SOCKET)
{printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if(bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == SOCKET_ERROR )
{printf("server bind error\n");
return -1;
}
if(listen(server, 1) == SOCKET_ERROR )
{printf("server bind error\n");
return -1;
}
printf("server start success\n");
while(1)
{asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if(client == INVALID_SOCKET)
{printf("client accept error\n");
return -1;
}
printf("client: %d\n", client);
do
{r = recv(client, buf, sizeof(buf), 0);
if(r > 0)
{printf("Receive: %s\n", buf);
if(strcmp(buf, "quit") != 0 )
{len = send(client, buf, r, 0);
}
else
{break;}
}
} while (r > 0);
closesocket(client);
}
closesocket(server);
WSACleanup();
return 0;
}
问题:select() 是 Linux 零碎特有的吗?
Windows 下的 select() 函数
- Windows 中同样提供 select() 函数,并且参数与 Linux 的版本完全相同
- 留神:Windows 中 select() 函数,第一个参数没有任何意义(仅为了兼容)
#include <winsock2.h>
int select(int nfds, fd_set *readfds,
fd_set *writefds,
fd_set *excepfds,
const struct timeval *timeout);
一个细微差别:Windows 中的 select() 专门为套接字设计
- fd_count 用于记录趣味的 socket 数量
- fd_array 用于记录感兴趣的 socket 句柄
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
}fd_set;
Windows 中 select() 函数应用示例
temps = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
num = select(0, &temps, 0, 0, &timeout);
if (num > 0) {
unsigned int i = 0;
for (i=0; i <reads.fd_fount; ++i) {if (FD_ISSET(reads.fd_array[i], &temps)) {if (reads.fd_array[i] == server) { }
else {}}
}
}
编程试验:Windows 中的多路复用服务端
// Select-Server.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <winsock2.h>
SOCKET server_handler(SOCKET server)
{struct sockaddr_in addr = {0};
int asize = sizeof(addr);
return accept(server, (struct sockaddr*)&addr, &asize);
}
int client_handler(SOCKET client)
{char buf[32] = {0};
int ret = recv(client, buf, sizeof(buf)-1, 0);
if(ret > 0)
{buf[ret] = 0;
printf("Receive: %s\n", buf);
if(strcmp(buf, "quit") != 0 )
{ret = send(client, buf, ret, 0);
}
else
{ret = -1;}
}
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
SOCKET server = 0;
struct sockaddr_in saddr = {0};
// unsigned int max = 0;
int num = 0;
fd_set reads = {0};
fd_set temps = {0};
struct timeval timeout = {0};
WSADATA wd = {0};
if(WSAStartup(MAKEWORD(2, 2), &wd) != 0 )
{printf("startup error\n");
return -1;
}
server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(server == INVALID_SOCKET)
{printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if(bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == SOCKET_ERROR )
{printf("server bind error\n");
return -1;
}
if(listen(server, 1) == SOCKET_ERROR )
{printf("server listen error\n");
return -1;
}
printf("server start success\n");
FD_ZERO(&reads);
FD_SET(server, &reads);
// max = server;
while(1)
{
temps = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
// num = select(max+1, &temps, 0, 0, &timeout);
num = select(0, &temps, 0, 0, &timeout);
if(num > 0)
{
unsigned int i = 0;
for(i=0; i<reads.fd_count; i++) // 此处绝对 Linux 的劣势,只需遍历理论监听的 socket 句柄数量!!{SOCKET sock = reads.fd_array[i];
if(FD_ISSET(sock, &temps) )
{if( sock == server)
{SOCKET client = server_handler(sock);
if(client != INVALID_SOCKET)
{FD_SET(client, &reads);
// max = (client > max) ? client : max;
printf("accept client: %d\n", client);
}
}
else
{int r = client_handler(sock);
if(r == -1)
{FD_CLR(sock, &reads);
closesocket(sock);
}
}
}
}
}
}
closesocket(server);
WSACleanup();
return 0;
}
思考:如何编写能够跨平台编译运行的网络程序?