嵌入式Linux(二):Socket 通信
前言
嵌入式Linux实现TCP或UDP通信是使用Socket编程来实现。本文主要记录实现TCP UDP的细节。
(一):Socket编程相关函数
通过man命令可以查看相关函数的文档。
1.socket
socket可以返回一个socket一个endpoint用于通信,并返回一个整型用于描述这个endpoint,错误返回-1.
# man socket
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//实例
iSocketServer = socket(AF_INET,SOCK_STREAM, 0);
1.1 domain:
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
1.2 type(详细可看man socket)
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission
mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
1.3 protocol (一般直接取0)
The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0.
2.bind
该函数用于给socket绑定IP地址。
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//实例
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SEVEER_PORT); //htons host to net short
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; //接收任何IP
memset(tSocketServerAddr.sin_zero, 0, 8); //置零
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr,sizeof(struct sockaddr));
if (iRet==-1){
printf("bind error!\n");
return -1;
}
2.1 sockfd
socket函数返回标识socket的整数。
sockfd = socket(AF_INET,SOCK_STREAM, 0);
2.2 addr
改参数的输入的是
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
但一般用struct sockaddr_in:
sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
#include <arpa/inet.h>
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SEVEER_PORT); //htons host to net short
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; //接收任何IP
memset(tSocketServerAddr.sin_zero, 0, 8); //置零
iRet = bind(sockfd, (const struct sockaddr *)&tSocketServerAddr,sizeof(struct sockaddr));
2.3 addrlen
前面地址的大小。
sizeof(struct sockaddr)
2.4 返回值
成功返回0,失败返回-1
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
3.listen
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
//实例
iRet = listen(iSocketServer,BACKLOG);
3.1 sockfd
socket函数返回标识socket的整数。
sockfd = socket(AF_INET,SOCK_STREAM, 0);
3.2 backlog
最大连接数。
The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow.
3.3 返回值
成功返回0,失败返回-1
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
4.accept
服务端,在bind后,并且listen后,可以用这个**等待客户端连接,并保存客户端的地址信息到addr
The argument sockfd is a socket that has been created with socket(2), bound to a local address with bind(2), and is listening for connections after a listen(2).
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//实例
iAddrLen = sizeof(struct sockaddr);
iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
4.1 sockfd
服务端的socket函数返回标识socket的整数。
sockfd = socket(AF_INET,SOCK_STREAM, 0);
4.2 addr
用于缓存客户端的地址变量地址。
struct sockaddr_in tSocketClientAddr;
iAddrLen = sizeof(struct sockaddr);
iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
4.3 addrlen
前面地址的大小。
sizeof(struct sockaddr)
4.4 返回值
成功就返回连接的客户端的socket,失败返回-1
On success, these system calls return a nonnegative integer that is a file descriptor for the accepted socket. On error, -1 is returned, and errno is set appropriately.
5.recv
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
//实例
iRecvLen = recv(iSocketClient, buff, 999, 0);
5.1 sockfd
客户端标识符
5.2 buf
字符串缓存数组地址
5.3 len
读取信息长度
5.4 flags
0:常规操作,与read()相同
MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
MSG_OOB:指明发送的是带外信息
MSG_PEEK:可以查看可读的信息,在接收数据后不会将这些数据丢失
MSG_WAITALL:通知内核直到读到请求的数据字节数时,才返回。
5.5 返回值
返回接收到数据的长度,错误返回-1
These calls return the number of bytes received, or -1 if an error occurred.
6.send
向已连接的socket写入数据。
The send() call may be used only when the socket is in a connected state (so that the intended recipient is known). The only difference between send() and write(2) is the pres‐
ence of flags. With a zero flags argument, send() is equivalent to write(2)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//实例
iSendLen = send(iSocketClient,buff,strlen(buff),0);
6.1 sockfd
要写入socket的标识符
6.2 buf
要写入数据
6.3 len
写入数据长度
6.4 flags
flags取值有:
0: 与write()无异
MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
MSG_OOB:指明发送的是带外信息
6.5 返回值
On success, these calls return the number of bytes sent. On error, -1 is returned, and errno is set appropriately.
7.sendto
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
//实例
iAddrLen = sizeof(struct sockaddr);
iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,
(const struct sockaddr *)&tSocketServerAddr, iAddrLen);
8.recvfrom
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
//实例
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
9.close
#include <unistd.h>
int close(int fd);
close(iSocketClient);
10 常用函数
//整型转网络端口号
#include <arpa/inet.h>
htons(SERVER_PORT);
//字符地址转换成网络地址
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
inet_aton(argv[1], &tSocketServerAddr.sin_addr)
//网络地址转换成字符地址
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
inet_ntoa(tSocketClientAddr.sin_addr)
(二):TCP
TCP server
/*server.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#define SEVEER_PORT 8888 //port
#define BACKLOG 10 //max connection
int main(int argc, char **argv)
{
int iSocketServer;
int iSocketClient;
int iRet;
int iAddrLen;
int iClientNum=-1;
int iRecvLen;
unsigned char buff[1000];
//解决僵尸进程问题
signal(SIGCHLD,SIG_IGN);
//set address
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SEVEER_PORT); //htons host to net short
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; //接收任何IP
memset(tSocketServerAddr.sin_zero, 0, 8); //置零
//scoket
iSocketServer = socket(AF_INET,SOCK_STREAM, 0);
if (iSocketServer==-1){
printf("socket error!\n");
return -1;
}
//bind
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr,sizeof(struct sockaddr));
if (iRet==-1){
printf("bind error!\n");
return -1;
}
//listen
iRet = listen(iSocketServer,BACKLOG);
if(iRet==-1){
printf("listen error!\n");
}
//accept
while(1){
iAddrLen = sizeof(struct sockaddr);
iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if(iSocketClient!=-1){
iClientNum++;
printf("connect from client %d :%s\n",iClientNum,inet_ntoa(tSocketClientAddr.sin_addr));
//fork
if(!fork()){
while(1){
//recv
iRecvLen = recv(iSocketClient, buff, 999, 0);
if(iRecvLen<=0){
//close
close(iSocketClient);
return -1;
}else{
buff[iRecvLen]='\0';
printf("client %d recv:%s\n",iClientNum,buff);
}
}
}
}
}
//close
close(iSocketServer);
return 0;
}
TCP client
/*client.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#define SEVEER_PORT 8888 //port
int main(int argc, char **argv)
{
int iSocketClient;
int iRet;
int iSendLen;
unsigned char buff[1000];
if(argc!=2){
printf("Usage:\n");
printf("%s <server ip>\n",argv[0]);
return -1;
}
//set address
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SEVEER_PORT); //htons host to net short
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; //接收任何IP
if(inet_aton(argv[1],&tSocketServerAddr.sin_addr)==0){
printf("invalid server ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8); //置零
//scoket
iSocketClient = socket(AF_INET,SOCK_STREAM, 0);
if (iSocketClient==-1){
printf("socket error!\n");
return -1;
}
//connect
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr,sizeof(struct sockaddr));
if (iRet==-1){
printf("connect error!\n");
return -1;
}
//send
while(1){
if(fgets(buff,999,stdin)){
iSendLen = send(iSocketClient,buff,strlen(buff),0);
if(iSendLen<=0){
close(iSocketClient);
return -1;
}
}
}
return 0;
}
(三):UDP
server
/*server.c*/
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* bind
* sendto/recvfrom
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iSocketServer;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRet;
int iAddrLen;
int iRecvLen;
unsigned char ucRecvBuf[1000];
int iClientNum = -1;
iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == iSocketServer)
{
printf("socket error!\n");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero, 0, 8);
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("bind error!\n");
return -1;
}
while (1)
{
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
}
close(iSocketServer);
return 0;
}
client
/*client.c*/
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
/* socket
* connect
* send/recv
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
int iRet;
unsigned char ucSendBuf[1000];
int iSendLen;
int iAddrLen;
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
{
printf("invalid server_ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
#if 0
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("connect error!\n");
return -1;
}
#endif
while (1)
{
if (fgets(ucSendBuf, 999, stdin))
{
#if 0
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
#else
iAddrLen = sizeof(struct sockaddr);
iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,
(const struct sockaddr *)&tSocketServerAddr, iAddrLen);
#endif
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}
结语
上面的TCP,UDP实现是参考韦东山老师的视频里的代码的,https://www.bilibili.com/video/BV1w4411B7a4?p=66&vd_source=9385b7f8c739b9e3ef3f21ddaebd2eb9
TCP运行截图