如何在C中打开和使用套接字?[已关闭]

uqdfh47h  于 2023-02-11  发布在  其他
关注(0)|答案(6)|浏览(101)

已关闭。此问题需要超过focused。当前不接受答案。
**想要改进此问题吗?**更新此问题,使其仅关注editing this post的一个问题。

1年前关闭。
Improve this question
我想知道在网络编程的C语言中打开和写入数据到套接字的最简单和最有效的方法。

gdx19jrr

gdx19jrr1#

你是对的,在C语言中使用socket语法很难。相比之下,后来的语言如Java和Python让它变得很简单。我发现用C语言进行socket编程的最好教程是Beej's Guide to Network Programming。我建议你从头开始,以获得一个很好的概述,但如果你只是需要让一些代码工作 * 现在 *,你可以跳到标题为客户机-服务器背景的部分。
祝你好运!

9fkzdhlc

9fkzdhlc2#

POSIX 7最小可运行客户端服务器TCP示例

将两台计算机连接到一个局域网中,例如您的家庭WiFi网络。
使用以下命令在一台计算机上运行服务器:

./server.out

使用ifconfig获取服务器计算机的IP,例如192.168.0.10
在另一台计算机上,运行:

./client.out 192.168.0.10

现在在客户机上输入lines,服务器将返回递增1(ROT-1密码)的结果。

服务器.c

#define _XOPEN_SOURCE 700

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    int enable = 1;
    int i;
    int newline_found = 0;
    int server_sockfd, client_sockfd;
    socklen_t client_len;
    ssize_t nbytes_read;
    struct sockaddr_in client_address, server_address;
    unsigned short server_port = 12345u;

    if (argc > 1) {
        server_port = strtol(argv[1], NULL, 10);
    }

    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }

    server_sockfd = socket(
        AF_INET,
        SOCK_STREAM,
        protoent->p_proto
        /* 0 */
    );
    if (server_sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
        perror("setsockopt(SO_REUSEADDR) failed");
        exit(EXIT_FAILURE);
    }

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(server_port);
    if (bind(
            server_sockfd,
            (struct sockaddr*)&server_address,
            sizeof(server_address)
        ) == -1
    ) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(server_sockfd, 5) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    fprintf(stderr, "listening on port %d\n", server_port);

    while (1) {
        client_len = sizeof(client_address);
        client_sockfd = accept(
            server_sockfd,
            (struct sockaddr*)&client_address,
            &client_len
        );
        while ((nbytes_read = read(client_sockfd, buffer, BUFSIZ)) > 0) {
            printf("received:\n");
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n')
                newline_found;
            for (i = 0; i < nbytes_read - 1; i++)
                buffer[i]++;
            write(client_sockfd, buffer, nbytes_read);
            if (newline_found)
                break;
        }
        close(client_sockfd);
    }
    return EXIT_SUCCESS;
}

客户c

#define _XOPEN_SOURCE 700

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char **argv) {
    char buffer[BUFSIZ];
    char protoname[] = "tcp";
    struct protoent *protoent;
    char *server_hostname = "127.0.0.1";
    char *user_input = NULL;
    in_addr_t in_addr;
    in_addr_t server_addr;
    int sockfd;
    size_t getline_buffer = 0;
    ssize_t nbytes_read, i, user_input_len;
    struct hostent *hostent;
    /* This is the struct used by INet addresses. */
    struct sockaddr_in sockaddr_in;
    unsigned short server_port = 12345;

    if (argc > 1) {
        server_hostname = argv[1];
        if (argc > 2) {
            server_port = strtol(argv[2], NULL, 10);
        }
    }

    /* Get socket. */
    protoent = getprotobyname(protoname);
    if (protoent == NULL) {
        perror("getprotobyname");
        exit(EXIT_FAILURE);
    }
    sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* Prepare sockaddr_in. */
    hostent = gethostbyname(server_hostname);
    if (hostent == NULL) {
        fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
        exit(EXIT_FAILURE);
    }
    in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
    if (in_addr == (in_addr_t)-1) {
        fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
        exit(EXIT_FAILURE);
    }
    sockaddr_in.sin_addr.s_addr = in_addr;
    sockaddr_in.sin_family = AF_INET;
    sockaddr_in.sin_port = htons(server_port);

    /* Do the actual connection. */
    if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
        perror("connect");
        return EXIT_FAILURE;
    }
    while (1) {
        fprintf(stderr, "enter string (empty to quit):\n");
        user_input_len = getline(&user_input, &getline_buffer, stdin);
        if (user_input_len == -1) {
            perror("getline");
            exit(EXIT_FAILURE);
        }
        if (user_input_len == 1) {
            close(sockfd);
            break;
        }
        if (write(sockfd, user_input, user_input_len) == -1) {
            perror("write");
            exit(EXIT_FAILURE);
        }
        while ((nbytes_read = read(sockfd, buffer, BUFSIZ)) > 0) {
            write(STDOUT_FILENO, buffer, nbytes_read);
            if (buffer[nbytes_read - 1] == '\n') {
                fflush(stdout);
                break;
            }
        }
    }
    free(user_input);

    exit(EXIT_SUCCESS);
}

在Ubuntu 15.10上测试。

消息长度

客户端和服务器上的read调用都在while循环内运行。
就像从文件中阅读一样,操作系统可以任意地分割消息以使事情更快,例如,一个数据包可能比另一个数据包到达得早得多。
因此协议必须指定消息停止的约定。常见的方法包括:

  • 带有长度指示符的报头(例如HTTP Content-Length
  • 终止消息的唯一字符串。这里我们使用\n
  • 服务器关闭连接:HTTP允许https://stackoverflow.com/a/25586633/895245。当然是有限的,因为下一条消息需要重新连接。
    后续步骤

此示例的局限性在于:

  • 服务器一次只能处理一个客户端连接
  • 简单地同步通信。例如:在P2P聊天应用程序上,服务器(其他人)可以随时发送消息。

解决这些问题需要线程处理,还可能需要poll之类的其他调用。

slhcrj9b

slhcrj9b3#

你没有提到你使用的是什么平台,但是Stevens的Unix Network Programming的副本会是你书架上的一个很好的补充,大多数操作系统都使用socket、bind、connect等来实现Berkley Sockets。

dsekswqp

dsekswqp4#

除非你写了一个网络守护程序,否则大多数用C语言建立的网络都可以在比直接使用套接字更高的层次上通过使用适当的库来完成。
例如,如果你只是想用HTTP来检索一个文件,那么就使用Neonlibcurl,这样会更简单,层次更高,而且你会有免费的SSL,IPv6等等。

cxfofazt

cxfofazt5#

从基本套接字阅读并不比读写普通文件困难(只要用recv代替read,用send代替write)。当你需要打开一个套接字时,事情会变得有点复杂。原因是有很多不同的方式使用套接字进行通信(TCP,UDP等)。

s2j5cfk0

s2j5cfk06#

我通常用C编写,但你可以在我写的白色“如何避免十大套接字编程错误”中找到一些用处-忽略使用ACE工具包的建议(因为它需要C),但要注意白皮书中的套接字错误-它们很容易犯,但很难找到,特别是对于初学者。http://www.riverace.com/sockets10.htm

相关问题