📜  C C++中的套接字编程(1)

📅  最后修改于: 2023-12-03 15:13:44.694000             🧑  作者: Mango

C/C++中的套接字编程

什么是套接字

在计算机网络中,套接字(Socket)是一个抽象层,用于表示在网络上的数据通信的一个端点。套接字可以简单地理解为通信中的两个程序之间的一个传输点。

在本文中,我们主要介绍C/C++中的套接字编程。

C/C++中的套接字

C/C++中的套接字编程又称为socket编程。C/C++中使用socket API函数来操作套接字。

socket函数

socket函数用于创建一个套接字,其原型为:

int socket(int domain, int type, int protocol);

参数说明:

  • domain:协议域,常用的协议有AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNIX(本地通信)、AF_NETLINK(Linux内核与用户空间通信)等。
  • type:套接字类型,常用的有SOCK_STREAM(流式套接字,可靠传输)和SOCK_DGRAM(数据报套接字,无连接、不可靠、面向数据报)。
  • protocol:通信协议,常用的有IPPROTO_TCP(TCP协议)和IPPROTO_UDP(UDP协议)。

返回值说明:

  • 成功:返回一个套接字的文件描述符。
  • 失败:返回-1。
bind函数

bind函数用于将一个套接字与一个IP地址和端口号绑定,其原型为:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

  • sockfd:套接字的文件描述符。
  • addr:指向一个存储了IP地址和端口号的结构体的指针(如struct sockaddr_in)。
  • addrlen:结构体的长度,可以用sizeof(addr)或sizeof(struct sockaddr)计算。

返回值说明:

  • 成功:返回0。
  • 失败:返回-1。
listen函数

listen函数用于将一个套接字设置为监听状态,即可以开始接收客户端的连接请求,其原型为:

int listen(int sockfd, int backlog);

参数说明:

  • sockfd:套接字的文件描述符。
  • backlog:客户端连接请求队列的最大长度,超过该长度的请求将被忽略。

返回值说明:

  • 成功:返回0。
  • 失败:返回-1。
accept函数

accept函数用于接收客户端的连接请求,创建一个新的套接字用于与客户端进行通信,其原型为:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数说明:

  • sockfd:套接字的文件描述符。
  • addr:指向一个存储了客户端IP地址和端口号的结构体的指针(如struct sockaddr_in)。
  • addrlen:结构体的长度,可以用sizeof(addr)或sizeof(struct sockaddr)计算。

返回值说明:

  • 成功:返回一个新套接字的文件描述符,用于与客户端进行通信。
  • 失败:返回-1。
connect函数

connect函数用于建立与服务器的连接,其原型为:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

  • sockfd:套接字的文件描述符。
  • addr:指向存储了服务器IP地址和端口号的结构体的指针(如struct sockaddr_in)。
  • addrlen:结构体的长度,可以用sizeof(addr)或sizeof(struct sockaddr)计算。

返回值说明:

  • 成功:返回0。
  • 失败:返回-1。
send和recv函数

send函数用于向已连接的套接字发送数据,其原型为:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数说明:

  • sockfd:套接字的文件描述符。
  • buf:要发送的数据的缓冲区。
  • len:要发送的数据的长度。
  • flags:发送标志,常用的有MSG_DONTWAIT(非阻塞发送)和MSG_NOSIGNAL(当对方关闭连接后不会产生SIGPIPE信号)。

返回值说明:

  • 成功:返回已发送的字节数。
  • 失败:返回-1。

recv函数用于从已连接的套接字接收数据,其原型为:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数说明:

  • sockfd:套接字的文件描述符。
  • buf:接收数据的缓冲区。
  • len:要接收的数据的长度。
  • flags:接收标志,常用的有MSG_DONTWAIT(非阻塞接收)。

返回值说明:

  • 成功:返回已接收的字节数。
  • 失败:返回-1。
示例代码

以下代码为C语言实现的简单TCP服务器,监听端口为5555,接收客户端发送的数据并打印:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main()
{
    int sockfd, newsockfd;
    socklen_t clilen;
    char buffer[1024];
    struct sockaddr_in serv_addr, cli_addr;
    int n;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("ERROR opening socket");
        exit(1);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(5555);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("ERROR on binding");
        exit(1);
    }

    listen(sockfd, 5);
    clilen = sizeof(cli_addr);

    while(1)
    {
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0)
        {
            perror("ERROR on accept");
            exit(1);
        }

        bzero(buffer, 1024);
        n = read(newsockfd, buffer, 1023);
        if (n < 0)
        {
            perror("ERROR reading from socket");
            exit(1);
        }

        printf("Here is the message: %s\n", buffer);

        n = write(newsockfd, "I got your message", 18);
        if (n < 0)
        {
            perror("ERROR writing to socket");
            exit(1);
        }

        close(newsockfd);
    }

    close(sockfd);
    return 0;
}

以上代码为C++语言实现的简单TCP客户端,连接服务器IP为"127.0.0.1",端口为5555,向服务器发送"Hello, World!"并接收服务器返回的数据:

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    int sockfd;
    char buffer[1024];
    struct sockaddr_in serv_addr;
    int n;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("ERROR opening socket");
        exit(1);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serv_addr.sin_port = htons(5555);

    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    {
        perror("ERROR connecting");
        exit(1);
    }

    bzero(buffer, 1024);
    strcpy(buffer, "Hello, World!");
    n = write(sockfd, buffer, strlen(buffer));
    if (n < 0)
    {
        perror("ERROR writing to socket");
        exit(1);
    }

    bzero(buffer, 1024);
    n = read(sockfd, buffer, 1023);
    if (n < 0)
    {
        perror("ERROR reading from socket");
        exit(1);
    }

    printf("Server says: %s\n", buffer);

    close(sockfd);
    return 0;
}