Windows网络通信(一):socket同步编程

网络通信常用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 1;
    }

    // 指定连接端口和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 1;
    }

    if (ConnectSocket == INVALID_SOCKET) {
        printf("无法连接到指定服务端!\n");
        WSACleanup();
        return 1;
    }

    // 发送一段数据
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("发送数据失败: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    printf("已发送数据大小: %ld\n", iResult);

    // 关闭发送功能,但是仍然可以接收
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("关闭发送功能失败: %d\n", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }

    // 接收数据
    do {

        iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0)
            printf("已接收: %d\n", iResult);
        else if (iResult == 0)
            printf("连接关闭\n");
        else
            printf("接收数据失败: %d\n", WSAGetLastError());

    } while (iResult > 0);

    //关闭套接字
    closesocket(ConnectSocket);
    WSACleanup();
    return 0;
}

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 1;
    }
    // 设定绑定的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 1;
    }

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("套接字监听失败: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // 接受来自客户端的连接
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    // 关闭服务端套接字,表示不需要再接收新的客户端连接了,但是已经连接的套接字还是能通信
    closesocket(ListenSocket);

    do {
        //接收来自客户端的消息
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("已接收数据大小: %d\n", iResult);

            // 发送数据到客户端,这里就是将数据
            iSendResult = send(ClientSocket, sendbuf, (int)strlen(sendbuf), 0);
            if (iSendResult == SOCKET_ERROR) {
                printf("发送失败: %d\n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }
            printf("发送字节大小: %d\n", iSendResult);
        }
        else if (iResult == 0)
            printf("连接已关闭\n");
        else {
            printf("接收数据失败: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }
    } while (iResult > 0);

    //关闭套接字以及清理套接字环境
    closesocket(ClientSocket);
    WSACleanup();

    return 0;
}
int main(int argc, char *argv[])
{
    int iResult;
    WSADATA wsaData;
    //初始化套接字环境
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("初始化socket环境失败: %d\n", iResult);
        return 1;
    }
    if (argc == 2 && strcmp(argv[1], "c") == 0)
    {
        //客户端
        return startClient();
    }
    else if (argc == 2 && strcmp(argv[1], "s") == 0)
    {
        //服务端
        return startServer();
    }
    return 1;
}

运行结果

image

 

后记

以上只是一个简单的socket通信示例,所有api调用都是阻塞的,非阻塞调用将在下文写出。

代码下载

posted @ 2017-04-21 11:05  reyzal  阅读(707)  评论(0编辑  收藏  举报