套接字概述
在Linux中,一切都是文件. Linux 中的网络编程通过socket接口进行, socket是一种特殊的I/0接口, 也是一种文件描述符. 常用的进程之间通信. 下图是使用TCP协议的通信过程 :
三次握手建立连接
四次挥手断开连接
这里通过实现两个例子了解一些常用API的用法, 不废话, 直接上代码.
1.回声客户端
服务端代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<pthread.h> #include<unistd.h> #include<errno.h> #include<netdb.h> #include<sys/socket.h> #include<netinet/in.h> #include<sys/types.h> #define PORT 4321 //端口号 #define BUFFER_SIZE 1024 //缓冲区大小 #define MAX_QUE_CONN_NM 5 //最大请求队列 /* 处理客户端请求 */ void *conn_hand(void *arg){ int client_fd = *(int *)arg; int recvbytes, sendbytes; char buf_recv[BUFFER_SIZE] = {0}, buf_send[BUFFER_SIZE]; while(1){ /* 调用recv()函数, 接受客户端请求. 从缓冲区中读取. */ if((recvbytes = recv(client_fd, buf_recv, BUFFER_SIZE, 0)) == -1){ perror("recv"); break; } printf("receive from client: %s\n", buf_recv); /* 退出 */ if(strcmp(buf_recv, "quit") == 0) break; /* 向客户端发送数据 这里只是写入缓冲区, 由TCP协议发送至网络中*/ if(send(client_fd, buf_recv, recvbytes, 0) == -1){ perror("send"); break; } memset(buf_recv, 0, BUFFER_SIZE); } printf("terminating current connect...\n"); close(client_fd); pthread_exit(NULL); } int main(void){ struct sockaddr_in server_sockaddr, client_sockaddr; int sin_size; int sockfd, client_fd; /* 建立socket连接 */ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket"); exit(1); } printf("socket id=%d\n", sockfd); /* 设置sockaddr_in 结构体参数 */ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero),8) ; /* 允许重复本地址绑定套接字 */ int i = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); /* 绑定函数bind() */ if(bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) == -1){ perror("bind"); exit(1); } printf("bind success!\n"); /* 调用listen函数监听, 创建未处理请求队列 */ if(listen(sockfd, MAX_QUE_CONN_NM) == -1){ perror("listen"); exit(1); } printf("listening port:%d ...\n", PORT); sin_size = sizeof(client_sockaddr); /* 循环接收客户端请求 */ while(1){ pthread_t tid; client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size); /* 创建新线程处理客户端请求*/ if(pthread_create(&tid, NULL, conn_hand, &client_fd) == -1){ perror("pthread_create"); break; } } close(sockfd); exit(0); }
客户端代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<netdb.h> #include<errno.h> #include<sys/socket.h> #include<netinet/in.h> #include<sys/types.h> #define PORT 4321 //端口 #define BUFFER_SIZE 1024 //缓冲区大小 int main(int argc, char *argv[]){ int sockfd, sendbytes, recvbytes; char buf_recv[BUFFER_SIZE], buf_send[BUFFER_SIZE]; struct hostent *host; struct sockaddr_in serv_addr; if(argc != 2){ fprintf(stderr, "usage: ./client <ip address>\n"); exit(1); } /* 地址解析函数 */ if((host = gethostbyname(argv[1])) == NULL){ perror("gethostbyname"); exit(1); } /* 设置sockaddr_in结构体参数 */ serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero), 8); /* 创建socket */ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket"); exit(1); } /*发起连接 */ if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1){ perror("connect"); exit(1); } //循环发送请求 while(1){ /* 发送消息 */ printf("sent to server:"); scanf("%s", buf_send); if((sendbytes = send(sockfd, buf_send, strlen(buf_send), 0)) == -1){ perror("send"); exit(1); } if(strcmp(buf_send, "quit") == 0) break; /* 读取回送消息 */ if((recvbytes = recv(sockfd, buf_recv, BUFFER_SIZE, 0)) > 0){ printf("receive from server : %s\n", buf_recv); } } close(sockfd); exit(0); }
编译运行
服务端 $ gcc server.c -o server -lpthread $ ./server socket id=3 bind success! listening port:4321 ... receive from client: 1
客户端
$ gcc client.c -o client -lpthread $ ./client 127.0.0.1 sent to server:1 receive from server : 1 sent to server:quit
2.文件传输
服务端代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<netdb.h> #include<sys/socket.h> #include<netinet/in.h> #include<sys/types.h> #define PORT 4321 //端口号 #define BUFFER_SIZE 1024 //缓冲区大小 #define MAX_QUE_CONN_NM 5 //最大请求队列 int main(void){ struct sockaddr_in server_sockaddr, client_sockaddr; int sin_size, recvbytes; int sockfd, client_fd; FILE *fp = fopen("file.txt", "rb"); if(fp ==NULL){ perror("file"); exit(1); } /* 建立socket连接 */ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket"); exit(1); } printf("socket id=%d\n", sockfd); /* 设置sockaddr_in 结构体参数 */ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero),8) ; int i = 1; /* 允许重复使用本地地址与套接字绑定 */ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); /* 绑定函数bind() */ if(bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) == -1){ perror("bind"); exit(1); } printf("bind success!\n"); /* 调用listen函数监听, 创建未处理请求队列 */ if(listen(sockfd, MAX_QUE_CONN_NM) == -1){ perror("listen"); exit(1); } printf("listening port:%d ...\n", PORT); /* 发送数据 */ sin_size = sizeof(client_sockaddr); char buf_send[BUFFER_SIZE] = {0}; if((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size)) == -1){ perror("accept"); exit(1); } /* 向客户端发送数据 这里只是写入缓冲区, 由TCP协议发送至网络中*/ int count; while((count = fread(buf_send, 1, BUFFER_SIZE, fp)) > 0){ if(send(client_fd, buf_send, count, 0) == -1){ perror("send"); exit(1); } } /* 文件读取完毕, 断开输入流, 发送FIN数据包 */ shutdown(client_fd, 2); /* 阻塞, 一旦客户端接收完毕,server收到FIN包, 继续向下执行 */ recv(client_fd, buf_send, BUFFER_SIZE, 0); fclose(fp); close(client_fd); close(sockfd); exit(0); }
客户端代码
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<netdb.h> #include<errno.h> #include<sys/socket.h> #include<netinet/in.h> #include<sys/types.h> #define PORT 4321 //端口 #define BUFFER_SIZE 1024 //缓冲区大小 int main(int argc, char *argv[]){ int sockfd, sendbytes, recvbytes; struct hostent *host; struct sockaddr_in serv_addr; FILE *fp; fp = fopen("file_download.txt", "wb"); if(fp ==NULL){ perror("file"); exit(1); } if(argc < 2){ fprintf(stderr, "USAGE: ./client Hostname(or ip address) Text\n"); exit(1); } /* 地址解析函数 */ if((host = gethostbyname(argv[1])) == NULL){ herror("gethostbyname"); exit(1); } /* 设置sockaddr_in结构体参数 */ serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero), 8); /* 创建socket */ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ perror("socket"); exit(1); } /*发起连接 */ if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1){ perror("connect"); exit(1); } /* 接收文件 */ char buf_recv[BUFFER_SIZE] = {0}; while((recvbytes = recv(sockfd, buf_recv, BUFFER_SIZE, 0)) > 0){ fwrite(buf_recv, recvbytes, 1, fp); } printf("文件接收完毕\n"); fclose(fp); close(sockfd); exit(0); }