Windows 下的 socket 编程接口与 Linux 中几乎相同
不同之处
通过 WSAStartup() 初始化系统环境 (最先调用)
socket(),accept() 错误返回 INVALID_SOCKET (不可默认为 -1)
bind(),listen() 错误返回 SOCKET_ERROR (不可默认为 -1)
connect(),send(),recv() 错误返回 SOCKET_ERROR (不可默认为 -1)
通过 WSACleanup() 清除系统环境 (最后调用)
在工程属性中设置链接 ws2_32.lib
定义变量 WSADATA wd;
选择 socket 版本并初始化 WSAStartup(MAKEWORD(2,2),&wd);
client.c
#include
#include
#pragma warning(disable : 4996)
int main()
{
SOCKET sock = 0;
struct sockaddr_in addr = { 0 };
char input[32] = { 0 };
char buf[128] = { 0 };
int n = 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("socker 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 succeed\n");
while (1)
{
printf("Input: ");
scanf("%s", input);
send(sock, input, strlen(input) + 1, 0);
n = recv(sock, buf, sizeof(buf), 0);
if (n > 0)
{
printf("Receive data: %s\n", buf);
}
else
{
break;
}
}
closesocket(sock);
WSACleanup();
return 0;
}
server.c
#include
#include
#pragma warning(disable : 4996)
int main()
{
SOCKET server = 0;
struct sockaddr_in saddr = { 0 };
SOCKET client = 0;
struct sockaddr_in caddr = { 0 };
int csize = 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(AF_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("start to accept\n");
while (1)
{
csize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &csize);
if (client == INVALID_SOCKET)
{
printf("server accept error\n");
return -1;
}
printf("client = %d\n", client);
do
{
r = recv(client, buf, sizeof(buf), 0);
if (r > 0)
{
if (strcmp("quit", buf) == 0)
{
break;
}
else
{
printf("Receive data: %s\n", buf);
send(client, buf, strlen(buf) + 1, 0);
}
}
} while (r > 0);
closesocket(client);
}
closesocket(server);
WSACleanup();
return 0;
}
程序运行结果如下:
select() 是 Linux 系统特有的吗?
Windows 中同样提供 select() 函数,且参数和 Linux 的版本完全相同
注意:Windows 中的 select() 函数,第一个参数没有任何意义 (仅为了兼容)
Windows 中的 select() 专门为套接字而设计
select-server.c
#include
#include
#pragma warning(disable : 4996)
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)
{
int ret = -1;
char buf[32] = { 0 };
ret = recv(client, buf, sizeof(buf) - 1, 0);
printf("Receive: %s\n", buf);
if (ret > 0)
{
buf[ret] = '0';
if (strcmp(buf, "quit") != 0)
{
ret = send(client, buf, strlen(buf), 0);
}
else
{
ret = -1;
}
}
return ret;
}
int main()
{
SOCKET server = 0;
struct sockaddr_in saddr = { 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;
}
FD_ZERO(&reads);
FD_SET(server, &reads);
printf("start to accept\n");
while (1)
{
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
temps = reads;
num = select(0, &temps, NULL, NULL, &timeout);
if (num > 0)
{
for (int i = 0; i <= reads.fd_count; i++)
{
SOCKET sock = reads.fd_array[i];
if (FD_ISSET(sock, &temps))
{
if (sock == server)
{
SOCKET client = server_handler(server);
if (client != INVALID_SOCKET)
{
FD_SET(client, &reads);
printf("accept client: %d\n", client);
}
}
else
{
if (client_handler(sock) == -1)
{
FD_CLR(sock, &reads);
closesocket(sock);
}
}
}
}
}
}
closesocket(server);
WSACleanup();
return 0;
}
Windows 下的 select() 函数,根据所监听文件描述符的个数遍历,相较于 Linux 的实现,遍历的次数更少,效率更高。
程序运行结果如下:
如何编写跨平台编译运行的网络程序?