如何在C中打开和使用套接字?

我想知道用C编程语言打开和写入数据到套接字的最简单,最有效的方法,用于网络编程。

你是对的,在C中使用套接字有一个困难的语法。 像Java和Python这样的后来的语言比较简单。 我在C中进行套接字编程的最佳教程是Beej的网络编程指南 。 我建议您从头开始以获得良好的概述,但如果您只需要使用一些代码,则可以跳到标题为“ 客户端 – 服务器背景 ”的部分。

祝好运!

你没有提到你所使用的平台,但史蒂文斯的Unix网络编程副本将是你书架的一个很好的补充。 大多数操作系统使用套接字,绑定,连接等实现Berkley套接字。

除非您编写网络守护程序,否则通过使用适当的库,C中的大多数网络可以比直接使用套接字更高级别完成。

例如,如果您只想使用HTTP检索文件,请使用Neon或libcurl 。 它会更简单,它将在更高的级别,你将免费提供SSL,IPv6等。

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

在局域网中获取两台计算机。

在一台计算机上运行服务器:

 ./server.out 

使用ifconfig获取服务器计算机的IP,例如192.168.0.10

在另一台计算机上,运行:

 ./client.out 192.168.0.10 

现在在客户端上键入行,服务器将返回递增1(ROT-1 cypher)。

server.c

 #define _XOPEN_SOURCE 700 #include  #include  #include  #include  #include  #include  /* getprotobyname */ #include  #include  #include  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; } 

client.c

 #define _XOPEN_SOURCE 700 #include  #include  #include  #include  #include  #include  #include  /* getprotobyname */ #include  #include  #include  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); } 

在GitHub上使用Makefile 。 在Ubuntu 15.10上测试过。

消息长度

客户端和服务器上的read调用在while循环中运行。

就像从文件读取时一样,OS可以任意分割消息以使事情更快,例如,一个数据包可能比另一个更早到达。

因此协议必须指定消息停止的约定。 常用方法包括:

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

下一步

这个例子是有限的,因为:

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

解决这些问题需要线程和可能的其他调用,如poll

从基本套接字读取和写入并不比读取和写入普通文件更难(如果写入,只需使用recv而不是读取和发送)。 当你需要打开一个sockets时,事情变得有点小。 原因是因为使用套接字(TCP,UDP等)进行通信的方式有很多种。

您可能想尝试Tcp4u,它是免费的任何使套接字编程非常容易。

http://www.jounin.net/tcp4u.html

到目前为止,这里有很好的建议。 我一般用C ++编写,但你可以在我写的“如何避免十大套接字编程错误”的白皮书中找到一些用法 – 忽略使用ACE工具包的建议(因为它需要C ++)但注意套接字论文中的错误 – 它们很容易制作而且很难找到,特别是对于初学者而言。 http://www.riverace.com/sockets10.htm

Linux网络编程权威指南简单地描述了socket,并解释了很多东西,比如服务器线程,协议设计等…… C,第二版中的TCP / IP套接字也很好。