网络通信常用API
1. WSAStartup用于初始化WinSock环境
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);wVersionRequested:当前进程能够使用Windows Socket的最高版本,目前指定2.2即可。
lpWSAData:指向一个WSAData结构体,接受Socket详细信息。
成功返回0
2. socket建立一个指定类型的SOCKET用于通信
SOCKET WSAAPI socket(
int af,
int type,
int protocol
);af:address family,指定用于通信的网络地址类型,可以取值AF_INET(IPv4),AF_INET6(IPv6),AF_BTH(蓝牙)等。
type:指定传输类型,可以取值SOCK_STREAM(用于TCP),SOCK_DGRAM(用于UDP)等。
protocol:通信协议,可以取值IPPROTO_TCP,IPPROTO_UDP等。
成功返回一个可以用于通信的SOCKET,否则返回INVALID_SOCKET。
3. bind将socket和网络地址和端口绑定起来
int bind(
SOCKET s,
const struct sockaddr *name,
int namelen
);s:一个未绑定的socket。
name:指向一个sockaddr对象,用于指定绑定的ip和端口信息。
namelen:sockaddr的长度,为什么这里还需要指定长度呢,因为name是根据socket的类型来指定不同的结构体的,可能是sockaddr_in(IPv4)或者sockaddr_in6(IPv6)。
成功返回0
4. listen将SOCKET设为监听状态,可以被客户端连接
int listen(
SOCKET s,
int backlog
);s:一个未被连接的socket
backlog:可以连接的客户端的最大数目,如果指定为SOMAXCONN,则设置为最大的连接数量。
成功返回0
5. send通过指定socket发送数据
int send(
SOCKET s,
const char *buf,
int len,
int flags
);s:一个已经连接的socket。
buf:待发送数据
len:待发送数据的长度
flags:发送的一个标志设定,一般设为0
成功返回已发送的字节数目。这个数目可能小于len的。失败返回SOCKET_ERROR。
6. recv通过指定的socket接受数据
int recv(
SOCKET s,
char *buf,
int len,
int flags
);s:一个已经连接的socket
buf:接收数据的缓存区
len:缓存区长度
flags:接受数据的一个标志,一般设为0。
成功返回已接受数据的长度,失败返回SOCKET_ERROR,如果已经断开连接,返回0
7. shutdown关闭一个SOCKET的send或者recv功能
int shutdown(
SOCKET s,
int how
);s:socket
how:指定该socket的某个功能不需要再使用,可以取值SD_RECEIVE(接收功能),SD_SEND(发送功能),SD_BOTH(发送和接收功能)。
成功返回0,失败返回SOCKET_ERROR
8. connect连接到服务端,服务端开启listen后,客户端就可以使用connect进行连接
int connect(
SOCKET s,
const struct sockaddr *name,
int namelen
);s:一个未连接的socket
name,namelen:和bind中name,namelen参数一样
成功返回0,失败返回SOCKET_ERROR
9. closesocket关闭一个已经存在的socket
int closesocket(
SOCKET s
);s:一个待关闭的socket。
成功返回0,失败返回SOCKET_ERROR
10. accept接收一个来自客户端的连接
SOCKET accept(
SOCKET s,
struct sockaddr *addr,
int *addrlen
);s:一个已经listen的socket
addr:用于储存接收到的客户端的sockaddr信息
addrlen:连接的客户端的sockaddr长度。
socket通信示例
服务端和客户端测试代码
// NetWork1.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT 12345 //启动客户端
int startClient()
{
SOCKET ConnectSocket = INVALID_SOCKET;
struct sockaddr_in clientService;
char *sendbuf = "[Client]:客户端测试文本";
char recvbuf[DEFAULT_BUFLEN];
int iResult;
int recvbuflen = DEFAULT_BUFLEN; // 创建一个TCP套接字
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket创建失败: %ld\n", WSAGetLastError());
WSACleanup();
return ;
} // 指定连接端口和ip信息
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
clientService.sin_port = htons(DEFAULT_PORT); // 连接服务端
iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (iResult == SOCKET_ERROR) {
printf("连接失败: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return ;
} if (ConnectSocket == INVALID_SOCKET) {
printf("无法连接到指定服务端!\n");
WSACleanup();
return ;
} // 发送一段数据
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), );
if (iResult == SOCKET_ERROR) {
printf("发送数据失败: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return ;
} printf("已发送数据大小: %ld\n", iResult); // 关闭发送功能,但是仍然可以接收
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("关闭发送功能失败: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return ;
} // 接收数据
do { iResult = recv(ConnectSocket, recvbuf, recvbuflen, );
if (iResult > )
printf("已接收: %d\n", iResult);
else if (iResult == )
printf("连接关闭\n");
else
printf("接收数据失败: %d\n", WSAGetLastError()); } while (iResult > ); //关闭套接字
closesocket(ConnectSocket);
WSACleanup();
return ;
} int startServer()
{
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
sockaddr_in service;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
char *sendbuf = "[Server]:服务端测试文本"; // 创建TCP套接字
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSocket == INVALID_SOCKET) {
printf("socket创建失败: %ld\n", WSAGetLastError());
WSACleanup();
return ;
}
// 设定绑定的ip和端口信息
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(DEFAULT_PORT); // 绑定套接字
iResult = bind(ListenSocket, (SOCKADDR *)&service, sizeof(service));
if (iResult == SOCKET_ERROR) {
printf("套接字绑定失败:%d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return ;
} iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("套接字监听失败: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return ;
} // 接受来自客户端的连接
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return ;
} // 关闭服务端套接字,表示不需要再接收新的客户端连接了,但是已经连接的套接字还是能通信
closesocket(ListenSocket); do {
//接收来自客户端的消息
iResult = recv(ClientSocket, recvbuf, recvbuflen, );
if (iResult > ) {
printf("已接收数据大小: %d\n", iResult); // 发送数据到客户端,这里就是将数据
iSendResult = send(ClientSocket, sendbuf, (int)strlen(sendbuf), );
if (iSendResult == SOCKET_ERROR) {
printf("发送失败: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return ;
}
printf("发送字节大小: %d\n", iSendResult);
}
else if (iResult == )
printf("连接已关闭\n");
else {
printf("接收数据失败: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return ;
}
} while (iResult > ); //关闭套接字以及清理套接字环境
closesocket(ClientSocket);
WSACleanup(); return ;
}
int main(int argc, char *argv[])
{
int iResult;
WSADATA wsaData;
//初始化套接字环境
iResult = WSAStartup(MAKEWORD(, ), &wsaData);
if (iResult != ) {
printf("初始化socket环境失败: %d\n", iResult);
return ;
}
if (argc == && strcmp(argv[], "c") == )
{
//客户端
return startClient();
}
else if (argc == && strcmp(argv[], "s") == )
{
//服务端
return startServer();
}
return ;
}
运行结果
后记
以上只是一个简单的socket通信示例,所有api调用都是阻塞的,非阻塞调用将在下文写出。