socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
相对于流套接字,数据报套接字的使用更为简单,它是由类型SOCK_DGRAM指定的,它不需要建立连接和维持一个连接,它们在AF_INET中通常是通过UDP/IP协议实现的。它对可以发送的数据的长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失、复制或错乱到达,UDP不是一个可靠的协议,但是它的速度比较高,因为它并一需要总是要建立和维持一个连接。
与使用流套接字一样,首先服务器应用程序用系统调用socket()来创建一个套接安,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。
接下来,服务器进程会给套接字起个名字(监听),我们使用系统调用bind()来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。
不同的是,然后系统调用recvfrom()来接收来自客户程序发送过来的数据。服务器程序对数据进行相应的处理,再通过系统调用sendto()把处理后的数据发送回客户程序。
与流套接字程序相比:
与流套接字程序相比:
int recvfrom(int sockfd, void *buffer, size_t len,int flags, struct sockaddr *src_from, socklen_t *src_len);
int sendto(int sockfd, void *buffer, size_t len, int flags, struct sockaddr *to, socklen_t tolen);
server.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <signal.h> int main(int arc, char **argv) { int server_sockfd = -1; socklen_t server_len = 0; socklen_t client_len = 0; char buffer[512]; ssize_t result = 0; struct sockaddr_in server_addr; struct sockaddr_in client_addr; // 创建数据报套接字 server_sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 设置监听的端口、IP server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(9739); server_len = sizeof(server_addr); // 绑定(命名)套接字 bind(server_sockfd, (struct sockaddr *)&server_addr, server_len); // 忽略子进程停止 或 退出 的信息,子进程成为僵尸进程后将由内核去管理 signal(SIGCHLD, SIG_IGN); while (1) { // 接收数据,用 client_addr 来储存数据来源程序的IP端口 // 函数会阻塞知道收到客户端发来的数据 result = recvfrom(server_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, &client_len); if (fork() == 0) { // 利用子进程来处理数据 buffer[0] += 'a' - 'A'; // 发送处理后的数据 sendto(server_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client_addr, client_len); printf("%c\n", buffer[0]); // 注意,一定要关闭子进程,否则程序运行会不正常 exit(0); } } // 关闭套接字 close(server_sockfd); }
client.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char **argv) { struct sockaddr_in server_addr; socklen_t server_len = 0; int sockfd = -1; char c = 'A'; // 取第一个参数的第一个字符 if (argc > 1) { c = argv[1][0]; } // 创建数据报套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 设置服务器IP、端口 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(9739); server_len = sizeof(server_addr); // 向服务器发送数据 sendto(sockfd, &c, sizeof(char), 0, (struct sockaddr *)&server_addr, server_len); // 接收服务器处理后发送过来的数据,由于不关心数据来源,所以把后两个参数设为0 recvfrom(sockfd, &c, sizeof(char), 0, 0, 0); printf("char from server = %c\n", c); // 关闭套接字 close(sockfd); exit(0); }