嵌入式Linux 网络编程
涉及到的数据结构:
下面首先介绍两个重要的数据类型:sockaddr和sockaddr_in,这两个结构类型都是用来保存socket地址信息的
定义如下所示:
struct sockaddr { unsigned short sa_family; /*地址族*/ char sa_data[14]; /*14字节的协议地址,包含该socket的IP地址和端口号*/ }; struct sockaddr_in { short int sa_family; /*地址族*/ unsigned short int sin_port; /*端口号*/ struct in_addr sin_addr; /*IP地址*/ unsigned char sin_zero[8]; /*填充0 以保持与struct sockaddr同样大小*/ };
这两个数据类型是等效的,可以相互转化,通常sockaddr_in数据类型使用更为方便。在建立socketaddr或sockaddr_in后,就可以对该socket进行适当的操作了。
如表所示列出了该结构sa_family字段可选的常见值:
涉及到的函数:
1. socket函数:创建套接字
socket函数标准调用格式说明如下:
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); //成功返回一个正整数,失败返回-1
- domain: 套接字的协议族,如:AF_INET(IPv4协议)、AF_INET6(IPv6协议)、AF_UNIX、AF_LOCAL(本地交互协议)等等
- type: 指定当前的套接字类型,如:SOCK_STREAM(数据流)、SOCK_DGRAM(数据报)、SOCK_RAW(原始套接字)等等
- protocol: 在使用原始套接字之外通常设置为0,表示使用默认协议
2. bind函数:绑定套接字
bind函数标准调用格式说明如下:
#include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, const struct socketaddr *addr, socklen_t addrlen); //成功返回0,否则返回-1
- sockfd: 使用socket函数创建的套接字对应的套接字描述符
- addr: 本地地址
- addrlen: 套接字对应的地址结构长度
3. connect函数:建立连接
connect函数标准调用格式说明如下:
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct socketaddr *addr, socklen_t addrlen); //成功返回0,否则返回-1
- sockfd: 使用socket函数创建的套接字对应的套接字描述符
- addr: 指定远程服务器的套接字地址,包括服务器的IP地址和端口号
- addrlen: 该套接字对应的地址结构长度
4. listen函数:倾听模式
listen函数标准调用格式说明如下:
#include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog); //成功返回0,否则返回-1
- sockfd: 使用socket函数创建的套接字对应的套接字描述符
- backlog: 设置请求队列的最大长度
5. accept函数:接收连接
accept函数标准调用格式说明如下:
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct socketaddr *addr, socklen_t *addrlen);
- sockfd: 使用socket函数创建的套接字对应的套接字描述符
- addr: 指向一个Internet套接字地址结构的指针
- addrlen: 指向一个整型变量的指针
6. close函数:关闭连接
close函数标准调用格式说明如下:
#include <unistd.h> int close(int fd);
7. send和recv函数:发送和接收数据
send和recv函数标准调用格式说明如下:
#include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags); //成功返回实际发送的字节数,否则返回-1
ssize_t recv(int sockfd, void *buf, size_t len, int flags); //成功返回实际接收的字节数,否则返回-1
- sockfd: 使用socket函数创建的套接字对应的套接字描述符
- buf: 待发送或接收数据的缓存区
- len: 待发送数据的缓存区的长度或接收数据的长度
- flags: 用于指定消息的传送类型
【应用实例】 —— Linux 系统 TCP网络协议编程
通过一个简单的 tcp 服务器端,接收客户端的连接请求,并接受客户端发来的信息。
服务器端和客户端使用TCP协议的流程图如图:
图 - 使用TCP协议socket编程流程图
其中服务器端首先建立起socket,然后调用本地端口的绑定,接着就开始与客户端建立联系,并接收客户端发送的消息。客户端则在建立socket之后调用connect函数来建立连接。
服务器端的源代码如下所示:
// server.c #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <time.h> #include <stdio.h> #include <string.h> #define PORT 5555 //服务器接听端口号 #define BUF_SIZE 1024 //缓存区大小 int main(int argc, char *argv[]) { int ret; char buf[BUF_SIZE]; int sockfd; int clientfd; struct sockaddr_in addr; struct sockaddr_in client_addr; int length = sizeof(client_addr); //创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if( sockfd == -1 ) { printf("socket fail..."); return 1; } //绑定套接字 bzero(&addr, sizeof(struct sockaddr)); addr.sin_family = AF_INET; //IPv4 addr.sin_port = htons(PORT); //设定端口号 addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地IP地址 ret = bind(sockfd, (struct sockaddr*)(&addr), sizeof(struct sockaddr)); if( ret == -1 ) { printf("bind fail..."); return 1; } //监听网络端口 ret = listen(sockfd, 5); if(ret == -1) { printf("listen fail..."); return 1; } while(1) { //接收连接请求 clientfd = accept(sockfd, (struct sockaddr*)(&client_addr), &length); if(clientfd == -1) { printf("accept fail..."); return 1; } while(1) { bzero(buf, sizeof(buf)); //清空缓存区 //接收数据 recv(clientfd, buf, sizeof(buf), 0); printf("recv: %s\n", buf); sleep(2); } close(clientfd); } return 0; }
客户端的源代码如下所示:
// client.c #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <time.h> #include <stdio.h> #include <string.h> #define PORT 5555 #define BUF_SIZE 1024 int main(int argc, char *argv[]) { int ret; char buf[BUF_SIZE]; int sockfd; struct sockaddr_in addr; //创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if( sockfd == -1 ) { printf("socket fail..."); return 1; } //建立连接 bzero(&addr, sizeof(struct sockaddr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); //使用回环地址 127.0.0.1 ret = connect(sockfd, (struct sockaddr*)(&addr), sizeof(struct sockaddr)); if( ret == -1 ) { printf("connect fail..."); return 1; } while(1) { bzero(buf, sizeof(buf)); printf("send: "); scanf("%s", buf); send(sockfd, buf, strlen(buf), 0); //发送数据 sleep(1); } close(sockfd); return 0; }