1 socket编程流程
1.1 创建socket
int socket(int domain, int type, int protocol);
domain 用于指定底层协议族,对 TCP/IP 协议族来说,应设置为 PF_INET(IPv4)或 PF_INET6(IPv6);type 用于指定服务类型,对 TCP/IP 族来说,SOCK_STREAM 表示使用 TCP 协议,SOCK_STREAM 表示使用 UDP 协议;protocol 是再选择一个具体的协议,一般设为 0 使用默认协议即可。
1.2 命名socket
在服务端,需要调用 bind 将 socket 与 socket 地址进行绑定,在客户端则不需要调用 bind 进行绑定,操作系统会自动分配地址,当然也可以自行调用 bind 进行地址绑定,但是没有那个必要。
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
所有专用 socket 地址(比如 TCP/IP 协议族的 sockaddr_in 和 sockaddr_in6 等)在使用时都需要转换为通用 socket 地址 sockaddr, 所以在传入第二个参数时往往需要进行强制转换。
1.3 监听socket
调用 listen 指定被监听的 socket 和监听队列的最大长度。
int listen(int sockfd, int backlog);
1.4 接受连接
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
1.5 发起连接
客户端通过 connect 与服务器建立连接。
int connect(int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen);
2 代码示例
2.1 服务端
#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<errno.h> #include<unistd.h> #include<sys/socket.h> #include<arpa/inet.h> const int BUFSIZE = 1024; int main(int argc, char* argv[]){ if(argc <= 3){ printf("usage: %s ip_adderss port number backlog\n", basename(argv[0])); return 1; } const char *ip = argv[1]; int port = atoi(argv[2]); //字符串转换整数 int backlog = atoi(argv[3]); /*创建socket*/ int sockfd = socket(PF_INET, SOCK_STREAM, 0); assert(sockfd >= 0); /*创建socket地址*/ struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; address.sin_port = htons(port); //主机字节序转换网络字节序 inet_pton(AF_INET, ip, &address.sin_addr); int ret = bind(sockfd, (struct sockaddr*) &address, sizeof(address)); //绑定socket地址 assert(ret != -1); /*监听socket*/ ret = listen(sockfd, backlog); assert(ret != -1); printf("Waiting for connection...\n"); struct sockaddr_in client; while(true){ socklen_t client_length = sizeof(client); /*accept连接*/ int connfd = accept(sockfd, (struct sockaddr *)&client, &client_length); if(connfd < 0){ printf("errno is: %d\n", errno); break; } printf("From client: IP is %s and Port is %d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); //输出客户端IP和端口 /*数据读写*/ char recv_buffer[BUFSIZE]; int n = recv(connfd, recv_buffer, BUFSIZE, 0); recv_buffer[n] = '\0'; printf("Got %d bytes of data: %s\n", n, recv_buffer); close(connfd); } close(sockfd); return 0; }
2.2 客户端
#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<errno.h> #include<unistd.h> #include<sys/socket.h> #include<arpa/inet.h> const int BUFSIZE = 1024; int main(int argc, char* argv[]){ if(argc <= 2){ printf("usage: %s ip_adderss port number\n", basename(argv[0])); return 1; } const char *ip = argv[1]; int port = atoi(argv[2]); //字符串转换整数 /*创建socket*/ int sockfd = socket(PF_INET, SOCK_STREAM, 0); assert(sockfd >= 0); /*创建socket地址*/ struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; address.sin_port = htons(port); //主机字节序转换网络字节序 inet_pton(AF_INET, ip, &address.sin_addr); /*发起连接*/ if(connect(sockfd, (struct sockaddr *)&address, sizeof(address)) < 0){ printf("Connection failed\n"); exit(1); } /*数据读写*/ printf("Please enter information to the server:\n"); char send_buffer[BUFSIZE]; fgets(send_buffer, BUFSIZE, stdin); if(send(sockfd, send_buffer, strlen(send_buffer), 0) < 0){ printf("errno is: %d\n", errno); exit(1); } close(sockfd); return 0; }