Socket(套接字)是网络通信的基石,它提供了进程间通信的端点。通过Socket,不同主机或同一主机上的不同进程可以进行数据交换。Socket本质上是一个编程接口(API),它封装了底层网络协议的细节,使开发者能够更方便地进行网络编程。
socket通过通信domain确定通信范围,使用的协议族等,目前常用的domain包括:
sockaddr
来表示不同协议族的地址: #include
struct sockaddr {
sa_family_t sa_family; // address family(AF_xxx)
char sa_data[]; // socket address
};
sa_family
字段指定地址族(如AF_INET、AF_INET6等)sa_data
字段包含实际的地址信息,其格式和长度由地址族决定sockaddr_in
),在调用函数时强制转换为sockaddr
类型 #include
int socket(int domain, int type, int protocol);
/*return file descriptor on success, or -1 on error*/
AF_INET(IPv4协议族);AF_INET6(IPv6协议族);AF_UNIX(UNIX域本地通信)
SOCK_STREAM(TCP流式socket);SOCK_DGRAM(UDP数据报socket)
type为SOCK_STREAM时,默认TCP;type为SOCK_DGRAM时,默认UDP
#include
int close(int sockfd);
/*return 0 on success or -1 on error*/
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*return 0 on success, or -1 on error*/
socketaddr_u(AF_UNIX); socketaddr_in(AF_INET); socketaddr_in6(AF_INET6)
流socket采用TCP协议,分为客户端和服务端:
系统调用listen()将socket标记为被动,准备接受来自客户端的连接请求:
#include
int listen(int sockfd, int backlog);
/*return 0 on success, or -1 on error*/
参数sockfd是已绑定的socket文件描述符;backlog指定等待连接队列的最大长度
系统调用accept()用于接受来自绑定地址的客户端的连接请求:
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*return new socket fd on success, or -1 on error*/
参数sockfd是监听socket的文件描述符;addr是监听socket的地址信息;addrlen是参数addr的大小
系统调用connect()用于发起与服务端的连接:
#include
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*return 0 on success, or -1 on error*/
参数sockfd是要连接的socket文件描述符;addr指定要连接的服务器地址;addrlen地址长度
系统调用recv()用于从已连接的socket接收数据:
#include
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
/*return number of bytes received, 0 on peer shutdown, or -1 on error*/
参数sockfd是已连接的socket文件描述符;buf指向接收缓冲区;len指定缓冲区大小;flags控制接收行为(通常设为0)
系统调用send()用于向已连接的socket发送数据:
#include
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
/*return number of bytes sent, or -1 on error*/
参数sockfd是已连接的socket文件描述符;buf指向发送数据缓冲区;len指定发送数据长度;flags控制发送行为(通常设为0)
数据报socket使用无连接的UDP协议进行通信:
系统调用recvfrom()用于从数据报socket接收数据并获取发送方地址:
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
/*return number of bytes received, or -1 on error*/
参数sockfd是socket文件描述符;buf指向接收缓冲区;len指定缓冲区大小;flags控制接收行为;src_addr返回发送方地址;addrlen指定地址结构体长度
系统调用sendto()用于向指定地址的数据报socket发送数据:
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
/*return number of bytes sent, or -1 on error*/
参数sockfd是socket文件描述符;buf指向发送数据;len指定数据长度;flags控制发送行为;dest_addr指定目标地址;addrlen指定地址结构体长度
Unix domain socket使用文件系统路径名作为地址,结构体定义如下:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* 路径名 */
};
sun_family固定为AF_UNIX;sun_path指定socket文件路径(最大107字符+NULL终止符)
/* Create a stream socket server */
int main(){
/* Create a stream socket */
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sfd == -1){
perror("socket");
return -1;
}
/* Remove any existing socket file */
unlink(SOCKET_ADDR_STREAM);
/* Set up server address structure */
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_ADDR_STREAM, strlen(SOCKET_ADDR_STREAM)+1);
/* Bind socket to address */
int err = bind(sfd, (struct sockaddr *)&addr, sizeof(addr.sun_path));
if(err == -1 ){
perror("bind");
close(sfd);
return -1;
}
/* Listen for incoming connections */
err = listen(sfd, SOMAXCONN);
if(err == -1){
perror("listen");
close(sfd);
return -1;
}
/* Client address structure and buffer */
struct sockaddr_un addr_cli;
socklen_t len_cli = sizeof(addr_cli);
char buff[1024];
ssize_t len;
/* Main server loop */
while(1){
/* Accept incoming connection */
int cfd = accept(sfd, (struct sockaddr *)&addr_cli, &len_cli);
if(cfd == -1){
perror("accept");
close(sfd);
return -1;
}
/* Receive data from client */
memset(buff, 0, sizeof(buff));
len = recv(cfd, buff, sizeof(buff), 0);
if(len == -1){
perror("recv");
close(sfd);
close(cfd);
return -1;
}
/* Print received message */
printf("recevied %zd bytes : \n %s \n", len, buff);
/* Check for exit command */
if(strcmp(buff,"exit") == 0){
printf("server closed, see you next time !\n");
break;
}
}
/* Clean up */
close(sfd);
unlink(SOCKET_ADDR_STREAM);
return 0;
}
/* Create a stream socket client */
int socket_stream_client(int argc, char *argv[]){
/* Check command line arguments */
if(argc < 2 || argv[1] == NULL){
printf("Usage:%s \n" , argv[0]);
return -1;
}
/* Create stream socket */
int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(sfd == -1){
perror("socket");
return -1;
}
/* Set up server address structure */
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_ADDR_STREAM, strlen(SOCKET_ADDR_STREAM)+1);
/* Connect to server */
int err = connect(sfd, (struct sockaddr *)&addr, sizeof(addr.sun_path));
if(err == -1){
perror("connect");
close(sfd);
return -1;
}
/* Send message to server */
ssize_t len = send(sfd, argv[1], strlen(argv[1]), 0);
if(len == -1){
perror("send");
return -1;
}
/* Clean up */
close(sfd);
return 0;
}
/* Create a datagram socket server */
int socket_dgram_server(){
/* Create a datagram socket */
int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(sfd == -1){
perror("socket");
return -1;
}
/* Remove any existing socket file */
unlink(SOCKET_ADDR_DGRAM);
/* Set up server address structure */
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_ADDR_DGRAM, strlen(SOCKET_ADDR_DGRAM)+1);
/* Bind socket to address */
int err = bind(sfd, (struct sockaddr *)&addr, sizeof(addr.sun_path));
if(err == -1){
perror("bind");
close(sfd);
return -1;
}
/* Buffer for received data */
char buff[1024];
/* Client address structure */
struct sockaddr_un cli_addr;
socklen_t len = sizeof(cli_addr);
/* Main server loop */
while(1){
memset(buff, 0, sizeof(buff));
/* Receive data from client */
ssize_t num_bytes = recvfrom(sfd, buff, sizeof(buff), 0,
(struct sockaddr *)&cli_addr, &len);
if(num_bytes == -1){
perror("recvfrom");
close(sfd);
return -1;
}
/* Print received message */
printf("received %zd bytes: %s\n", num_bytes, buff);
/* Check for exit command */
if(strcmp(buff, "exit") == 0){
printf("server closed, see you next time!\n");
break;
}
}
/* Clean up */
close(sfd);
unlink(SOCKET_ADDR_DGRAM);
return 0;
}
/* Create a datagram socket client */
int socket_dgram_client(int argc, char *argv[]){
/* Check command line arguments */
if(argc < 2 || argv[1] == NULL){
printf("Usage:%s \n" , argv[0]);
return -1;
}
/* Create datagram socket */
int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if(sfd == -1){
perror("socket");
return -1;
}
/* Set up server address structure */
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKET_ADDR_DGRAM, strlen(SOCKET_ADDR_DGRAM)+1);
/* Send message to server */
ssize_t len = sendto(sfd, argv[1], strlen(argv[1]), 0,
(struct sockaddr *)&addr, sizeof(addr.sun_path));
if(len == -1){
perror("sendto");
close(sfd);
return -1;
}
/* Clean up */
close(sfd);
return 0;
}