无连接的数据传输
用于无连接套接字的读写函数情况要复杂一点。由于没有建立一个连接,所以每次发送数据的过程中都要明确指明该数据包的地址,同时,在接收数据包的时候,接受进程能够得到发送该数据包的地址,从而知道该数据包从哪里来。
Linux环境中提供有专门对无连接的套接字进行读写的函数,这两个函数分别为sendto函数和recvfrom函数,函数原型如下:
int sendto(int sockfd, const *restrict buf, size_t len, int flags, struct sockaddr * restrict destaddr, socket_len *restrict destlen);
头文件: #include <sys/types.h> #include <sys/socket.h>
参数说明:
第一个参数sockfd表示通信用的套接字。
第二个参数buf表示发送内容所在的缓冲区。
第三个参数len表示需要发送的字节数。
第四个参数flags是传输标志位,一般设0,详细描述请参考send()。
第五个参数destaddr是一个sockaddr地址结构的地址,该地址表示数据报的目的地,结构sockaddr 请参考bind。
第六个参数destlen表示数据报目的地址结构大小。
返回值:成功返回实际传送出去的字符数,失败返回-1。
int recvfrom (int sockfd, const *restrict buf, size_t len, int flags, struct sockaddr * restrict addr, socket_len *restrict addrlen);
头文件: #include <sys/types.h> #include <sys/socket.h>
参数说明:
第一个参数sockfd表示通信用的套接字。
第二个参数buf表示存储接收数据的缓冲区。
第三个参数len表示需要接收的字节数。
第四个参数flags是传输标志位。
第五个参数addr是一个sockaddr地址结构的地址,该地址表示数据报的发送地址。
第六个参数addrlen表示数据报目的地址结构大小。
无连接的数据传说一般使用的是UDP协议,还是使用大小写字母转换的例子来演示无连接服务的服务器端程序。无连接服务器端执行流程如下:
服务器端程序代码如下所示:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <ctype.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <errno.h> #define MAX_LINE 100 /*处理函数,将大写字符转换成小写字符。参数为需要转换的字符串*/ void my_fun(char *p) { if(p == NULL) return ; for(; *p != '\0'; p++) if(*p >= 'A' && *p <= 'Z') *p = *p - 'A' + 'a'; } int main(void) { struct sockaddr_in sin; struct sockaddr_in cin; int s_fd; int port = 8000; socklen_t addr_len; char buf[MAX_LINE]; char addr_p[INET_ADDRSTRLEN]; int n; /*填写地址结构*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); /*使用UDP通信协议创建一个套接字,并且得到发送本数据包的客户端地址*/ s_fd = socket(AF_INET, SOCK_DGRAM, 0); if(s_fd == -1) { perror("fail to create socket"); exit(1); } /*将套接字绑定到一个地址*/ if(bind(s_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { perror("call to bind"); exit(1); } /*服务器是一个死循环,一次循环处理一个请求*/ while(1) { addr_len = sizeof(sin); /*UDP协议是无连接的,所以只有一个套接字接受客户端传输的数据报即可 *而不需要在创建一个套接字,用于建立连接 *同样的使用UDP协议的网络程序也不需要调用accept函数 **/ ///接收客户端传送过来的字符串 n = recvfrom(s_fd, buf, MAX_LINE, 0, (struct sockaddr *) &cin, &addr_len); if(n == -1) { perror("fail to receive"); exit(1); } /*将客户端地址转换成字符串*/ inet_ntop(AF_INET, &cin.sin_addr, addr_p, sizeof(addr_p)); printf("client IP is %s, port is %d\n", addr_p, ntohs(cin.sin_port)); ///打印客户端地址和端口号 printf("content is : %s\n", buf); ///打印客户端发送过来的字符串 my_fun(buf); /*使用sendto函数将转换后的字符串回传给客户端程序*/ n = sendto(s_fd, buf, n, 0, (struct sockaddr *) &cin, addr_len); if(n == -1) { perror("fail to send"); exit(1); } } if(close(s_fd) == -1) { perror("fail to close"); exit(1); } return 0; }
客户端程序代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define MAX_LINE 80 int main(int argc, char *argv[ ]) { struct sockaddr_in sin; struct sockaddr_in cin; int port = 8000; socklen_t addr_len; int s_fd; char *str = "test"; char buf[MAX_LINE]; char add_p[INET_ADDRSTRLEN]; int n; if(argc != 2) { printf("wrong command\n"); exit(1); } str = argv[1]; bzero(&sin, sizeof(sin)); /*使用IP地址族*/ sin.sin_family = AF_INET; /*与本机的服务器程序通信,使用本机的IP地址*/ inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr); sin.sin_port = htons(port); s_fd = socket(AF_INET, SOCK_DGRAM, 0); if(s_fd == -1) { perror("fail to create socket"); exit(1); } sprintf(buf, "%s\0", str); n = sendto(s_fd, buf, strlen(str) + 1, 0, (struct sockaddr *)&sin, sizeof(sin)); if(n == -1) { perror("fail to send\n"); exit(1); } addr_len = sizeof(cin); n = recvfrom(s_fd, buf, MAX_LINE, 0, (struct sockaddr *) &cin, &addr_len); if(n == -1) { perror("fail to receive"); exit(1); } else printf("receive from server : %s\n", buf); if(close(s_fd) == -1) { perror("fail to close"); exit(1); } return 0; }