本章函数大全
目录
2.7 shutdown() & close() - 关闭连接
在网络编程中,了解并掌握 socket、bind、listen、accept、connect、recv、send、shutdown、close 这些函数至关重要。本文将逐一解析它们的作用,并提供相应的示例代码和总结表格,帮助你快速掌握这些关键函数。
1. 网络编程基本流程
网络编程通常涉及以下步骤:
-
服务器端
- 创建
socket
- 绑定
bind
- 监听
listen
- 接收连接
accept
- 进行数据通信 (
recv/send
) - 关闭连接 (
shutdown/close
)
- 创建
-
客户端
- 创建
socket(绑定bind可有可无)
- 连接
connect
- 进行数据通信 (
recv/send
) - 关闭连接 (
shutdown/close
)
- 创建
2. 关键函数解析
2.1 socket()
- 创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain
: 地址协议族,如AF_INET
(IPv4) 或AF_INET6
(IPv6)。type
: 套接字类型,如SOCK_STREAM
(TCP) 或SOCK_DGRAM
(UDP)。protocol
: 通常为0
,表示使用默认协议。- 返回值: 成功返回套接字文件描述符,失败返回
-1
。
示例代码:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
2.2 bind()
- 绑定IP和端口(仅服务器端)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd
: 由socket()
创建的套接字文件描述符。addr
: 指向sockaddr
结构体的指针,包含IP和端口信息。addrlen
:addr
结构体的大小。- 返回值: 成功返回
0
,失败返回-1
。
示例代码:
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
exit(EXIT_FAILURE);
}
2.3 listen()
- 监听连接请求(仅服务器端)
int listen(int sockfd, int backlog);
sockfd
: 服务器的监听套接字。backlog
: 队列中最大连接数。- 返回值: 成功返回
0
,失败返回-1
。
示例代码:
if (listen(sockfd, 5) == -1) {
perror("listen failed");
exit(EXIT_FAILURE);
}
2.4 accept()
- 接收客户端连接(仅服务器端)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
示例代码:
struct sockaddr_in client_addr;
socklen_t addr_size = sizeof(client_addr);
int client_sock = accept(sockfd, (struct sockaddr*)&client_addr, &addr_size);
if (client_sock == -1) {
perror("accept failed");
exit(EXIT_FAILURE);
}
2.5 connect()
- 连接服务器(仅客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
示例代码:
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("connect failed");
exit(EXIT_FAILURE);
}
2.6 recv()
& send()
- 数据传输
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
示例代码:
char buffer[1024];
int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
if (bytes_received > 0) {
printf("Received: %s\n", buffer);
}
2.7 shutdown()
& close()
- 关闭连接
int shutdown(int sockfd, int how);
int close(int sockfd);
示例代码:
shutdown(client_sock, SHUT_WR);
close(client_sock);
3. 服务器 & 客户端示例代码
服务器代码
// server.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SOCKET_DOMAIN AF_INET // (域名)网络层端点协议:ipv4
#define SOCKET_TYPE SOCK_STREAM // 传输层协议:TCP
#define SERVER_SOCKADDR "127.0.0.1" // 设置服务端地址
#define SERVER_SOCKPORT 8888 // 设置服务端端口号
#define BACKLOG 5 // 如果连接数超过 BACKLOG 新连接请求将被拒绝(或延迟处理)
#define MAXLENGTH 256 // 最大字节长度
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void *read_from_client(void *arg)
{
int fd = *(int *)arg;
char *rbuf = calloc(MAXLENGTH+1,sizeof(char));
if(rbuf == NULL){
perror("calloc");
return NULL;
}
ssize_t rbytes = 0;
// flag=0:阻塞等待数据
while((rbytes = recv(fd, rbuf, MAXLENGTH+1, 0)) > 0){
printf("%.*s",(int)rbytes,rbuf);
}
if(rbytes == -1){
perror("recv");
}
printf("客户端请求关闭\n");
free(rbuf);
return NULL;
}
static void *write_to_client(void *arg)
{
int fd = *(int *)arg;
char *wbuf = calloc(MAXLENGTH+1,sizeof(char));
if(wbuf == NULL){
perror("calloc");
return NULL;
}
while(fgets(wbuf,MAXLENGTH,stdin)){
wbuf[MAXLENGTH] = 0;
if(send(fd,wbuf,strlen(wbuf),0) == -1){
perror("send");
break;
}
}
printf("服务端接收到控制台的关闭请求,不再写入,开始挥手\n");
shutdown(fd, SHUT_WR);
free(wbuf);
return NULL;
}
int main(int argc,char *argv[])
{
int sockfd,clientfd;
struct sockaddr_in client_sockaddr;
socklen_t client_sockaddr_len = sizeof(client_sockaddr);
// 服务端地址和端口初始化
struct sockaddr_in server_sockaddr;
memset(&server_sockaddr, 0, sizeof(server_sockaddr));
server_sockaddr.sin_family = SOCKET_DOMAIN;
inet_pton(SOCKET_DOMAIN, SERVER_SOCKADDR, &server_sockaddr.sin_addr);
// server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
server_sockaddr.sin_port = htons(SERVER_SOCKPORT);
pthread_t pthread_read;
pthread_t pthread_write;
// 客户端网络编程流程
// 1.为通信创建端点
sockfd = socket(SOCKET_DOMAIN,SOCKET_TYPE,0);
if(sockfd == -1)
handle_error("socket");
printf("服务端文件描述符:%d\n",sockfd);
// 2.创建好客户端地址结构,绑定
if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr)) < 0){
close(sockfd);
handle_error("bind");
}
// 3.监听
if(listen(sockfd, BACKLOG) == -1){
close(sockfd);
handle_error("listen");
}
printf("正在监听端口: %d\n",SERVER_SOCKPORT);
//4.阻塞等待连接
clientfd = accept(sockfd, (struct sockaddr *)&client_sockaddr,&client_sockaddr_len);
if(clientfd == -1){
close(sockfd);
handle_error("accept");
}
printf("有客户端进行连接: %s %d 文件描述符:%d\n",inet_ntoa(client_sockaddr.sin_addr),ntohs(client_sockaddr.sin_port),clientfd);
// 4.创建本地接收和发送数据线程
pthread_create(&pthread_read,NULL,read_from_client,(void *)&clientfd);
pthread_create(&pthread_write,NULL,write_to_client,(void *)&clientfd);
// 5.阻塞主线程
pthread_join(pthread_read,NULL);
pthread_join(pthread_write,NULL);
printf("服务端断开连接,释放资源\n");
close(clientfd);
close(sockfd);
return 0;
}
客户端代码
// client.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SOCKET_DOMAIN AF_INET // (域名)网络层端点协议:ipv4
#define SOCKET_TYPE SOCK_STREAM // 传输层协议:TCP
#define CLIENT_SOCKADDR "172.xxx.xxx.xxx" 这里填写自己的ip地址 // 设置本地地址
#define CLIENT_SOCKPORT 6666 // 设置本地端口号
#define SERVER_SOCKADDR "127.0.0.1" // 设置服务端地址
#define SERVER_SOCKPORT 8888 // 设置服务端端口号
#define MAXLENGTH 256 // 最大字节长度
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void *read_from_server(void *arg)
{
int fd = *(int *)arg;
char *rbuf = calloc(MAXLENGTH+1,sizeof(char));
if(rbuf == NULL){
perror("calloc");
return NULL;
}
ssize_t rbytes = 0;
// flag=0:阻塞等待数据
while((rbytes = recv(fd, rbuf, MAXLENGTH+1, 0)) > 0){
printf("%.*s",(int)rbytes,rbuf);
}
if(rbytes == -1){
perror("recv");
}
printf("服务端请求关闭\n");
free(rbuf);
return NULL;
}
static void *write_to_server(void *arg)
{
int fd = *(int *)arg;
char *wbuf = calloc(MAXLENGTH+1,sizeof(char));
if(wbuf == NULL){
perror("calloc");
return NULL;
}
while(fgets(wbuf,MAXLENGTH,stdin)){
wbuf[MAXLENGTH] = 0;
if(send(fd,wbuf,strlen(wbuf),0) == -1){
perror("send");
break;
}
}
printf("客户端接收到控制台的关闭请求,不再写入,开始挥手\n");
shutdown(fd, SHUT_WR);
free(wbuf);
return NULL;
}
int main(int argc,char *argv[])
{
int sockfd;
// 客户端地址和端口初始化
struct sockaddr_in client_sockaddr;
memset(&client_sockaddr, 0, sizeof(client_sockaddr));
client_sockaddr.sin_family = SOCKET_DOMAIN;
inet_pton(SOCKET_DOMAIN, CLIENT_SOCKADDR, &client_sockaddr.sin_addr);
client_sockaddr.sin_port = htons(CLIENT_SOCKPORT);
// 服务端地址和端口初始化
struct sockaddr_in server_sockaddr;
memset(&server_sockaddr, 0, sizeof(server_sockaddr));
server_sockaddr.sin_family = SOCKET_DOMAIN;
inet_pton(SOCKET_DOMAIN, SERVER_SOCKADDR, &server_sockaddr.sin_addr);
server_sockaddr.sin_port = htons(SERVER_SOCKPORT);
pthread_t pthread_read;
pthread_t pthread_write;
// 客户端网络编程流程
// 1.为通信创建端点
sockfd = socket(SOCKET_DOMAIN,SOCKET_TYPE,0);
if(sockfd == -1)
handle_error("socket");
printf("客户端文件描述符:%d\n",sockfd);
// 2.创建好客户端地址结构,绑定/可有可无
// if(bind(sockfd,(struct sockaddr *)&client_sockaddr,sizeof(client_sockaddr)) < 0){
// close(sockfd);
// handle_error("bind");
// }
// 3.创建好服务器地址结构,连接服务端
if(connect(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr)) < 0){
close(sockfd);
handle_error("connect");
}
printf("连接上服务器 %s %d\n",inet_ntoa(server_sockaddr.sin_addr),ntohs(server_sockaddr.sin_port));
// 4.创建本地接收和发送数据线程
pthread_create(&pthread_read,NULL,read_from_server,(void *)&sockfd);
pthread_create(&pthread_write,NULL,write_to_server,(void *)&sockfd);
// 5.阻塞主线程
pthread_join(pthread_read,NULL);
pthread_join(pthread_write,NULL);
printf("客户端断开连接,释放资源\n");
close(sockfd);
return 0;
}
注:示例程序为本地作为服务端与本地进行通信
4. 结论
函数 | 作用 |
---|---|
socket | 创建套接字 |
bind | 绑定地址 |
listen | 监听连接 |
accept | 接受连接 |
connect | 连接服务器 |
recv/send | 传输数据 |
shutdown | 关闭连接 |
close | 关闭套接字 |