(本节笔记的实验代码,在这里)
1. TCP编程模型函数化
1.1 server端函数
创建socket——>socket(); 绑定地址 ——>bind(); 监听端口 ——>listen(); 等待连接 ——>accept(); 接收数据 ——>recv(); 发送数据 ——>send(); 结束连接 ——>close(); 1.2 client端函数 创建socket——> socket(); 连接服务器 ——>connect(); 接收数据 ——>recv(); 发送数据 ——>send(); 结束连接 ——>close(); 2. 函数学习 2.1创建套接字 函数名: socket 函数原型: man socket int socket(int domain, int type, int protocol); 函数功能: 创建网络套接字。 所属头文件: <sys/types.h> <sys/socket.h> 返回值: 成功 :返回int类型的sockfd 失败 :返回-1 参数说明 domain :AF_INET(IPV4因特网域) AF_INET6(IPV6因特网域)。 type :SOCK_DGRAM(固定长度的无连接的不可靠的报文传输) SOCK_RAW(IP协议的数据报接口,在POSIX.1中为可选) SOCK_SEQPACKET(固定长度的有序的可靠的面向连接的报文传输) SOCK_STREAM(有序的可靠的双向的面向连接的字节流)。 protocol :通常是0。 2.2 绑定地址 函数名: bind 函数原型: man 2 bind int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 函数功能: 将套接字与地址关联。 所属头文件: <sys/types.h> <sys/socket.h> 返回值: 成功 :返回0 失败 :返回-1 参数说明 sockfd :需要绑定的套接字ID。 *addr :一个struct sockaddr结构的指针,里面存有server端地址。 //一般用法为(struct sockaddr *)(&struct sockaddr_in) addrlen :地址长度。 /*********************************************************** struct sockaddr{ //该结构是通用的地质类型,包括IPv4和IP6 sa_family_t sa_family; //协议族 char sa_data[14]; //地址 } struct sockaddr_in{ //该结构是IPv4专用地址结构 short int sin_family; //协议族,AF_INET unsigned short int sin_port; //端口号,2字节 struct in_addr sin_addr; //IPv4地址,4字节 unsigned char sin_zero[8]; //用0填充8字节 } struct in_addr{ unsigned long s_addr; //一个长整形的地址数据, } //需要把字符串转发为整形 *********************************************************** in_addr_t inet_addr(const char *cp); <sys/socket.h> <netinet.in.h> <arpa/inet.h> 功能:把字符串形式的IP地址转化为整形的IP地址(网络字节序)。 范例:in_addr.s_addr = inet_addr ("192.168.0.1") char *inet_ntoa(struct in_addr); 功能:把整形的IP地址转化为字符型的IP地址。 *********************************************************** 网络字节序: 网络上通讯过程中,需把发送到网络上的数据转化为网络字节序(大端模式数据),接收数据时,要把数据转化为主机字节序。 <arpa/inet.h> uint32_t htonl(uint32_t hostlong); 功能:将32位数据从主机字节序转化为网络字节序 范例:in_addr.s_addr = htonl(INADDR_ANY); uint16_t htons(uint16_t hostshort); 功能:将16位数据从主机字节序转化为网络字节序 uint32_t ntohl(uint32_t netlong); 功能:将32位数据从网络字节序转化为主机字节序 uint16_t ntohs(uint16_t netshort); 功能:将16位数据从网络字节序转化为主机字节序 ***********************************************************/ 2.3 设置监听端口个数 函数名: listen 函数原型: man listen int listen(int sockfd, int backlog); 函数功能: 设置监听端口个数,表示服务器愿意接受连接请求。 所属头文件: <sys/types.h> <sys/socket.h> 返回值: 成功 :返回0 失败 :返回-1 参数说明 sockfd :server端套接字ID。backlog :server端愿意接受连接的个数。
2.4 等待连接 函数名: accept 函数原型: man 2 accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 函数功能: 等待连接请求,接受并建立连接。若无连接请求则进入阻塞。 所属头文件: <sys/types.h> <sys/socket.h> 返回值: 成功 :返回新的sockfd 失败 :返回-1 参数说明 sockfd :server端的套接字ID。 *addr :一个struct sockaddr结构的指针,里面存有client端地址。 //一般用法为(struct sockaddr *)(&struct sockaddr_in) *addrlen :client端地址长度(变量)的地址。 2.5 接收数据 函数名: recv 函数原型: man recv ssize_t recv(int sockfd, void *buf, size_t len, int flags); 函数功能: 控制如何接收数据。 所属头文件: <sys/types.h> <sys/socket.h> 返回值: 成功 :返回接收数据的大小 失败 :返回-1 参数说明 sockfd :accept函数返回的新的sockfd。 *buf :接收的数据存放的地址。 len :接收的数据大小。 flags :标志位,如果不需要设置可以直接设为0。 2.6发送数据 函数名: send 函数原型: man send ssize_t send(int sockfd, const void *buf, size_t len, int flags); 函数功能: 控制如何发送数据。 所属头文件: <sys/types.h> <sys/socket.h> 返回值: 成功 :返回发送数据的大小 失败 :返回-1 参数说明: sockfd :accept函数返回的新的sockfd。。 *buf :需要发送的数据存放地址。 len :发送数据的大小。 flags :标志位,如果不需要设置可以直接设为0。 2.7 关闭连接 函数名: close 函数原型: man close
int close(int fd);
函数功能: 关闭连接。 所属头文件: <unistd.h> 返回值: 成功 :返回0 失败 :返回-1 参数说明 fd :sockfd。 2.8 连接服务器 函数名: connect 函数原型: man int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 函数功能: client端向server端请求建立连接。 所属头文件: <sys/types.h> <sys/socket.h> 返回值: 成功 :返回0 失败 :返回-1 参数说明 sockfd :client端sockid。 *addr :一个struct sockaddr结构的指针,里面存有server端地址。 //一般用法为(struct sockaddr *)(&struct sockaddr_in) addrlen :client端地址长度。 3. 综合实例 /* touch server.c *//* 利用TCP协议进行网络通讯的服务器端程序 */
#include <stdio.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #define PORTNUM 3333 int main() { int sockfd; int app_sockfd; int recv_sz; long addr_len; char buff[128]; struct sockaddr_in server_addr; struct sockaddr_in client_addr; /* 1.创建socket */ if(-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0))) { printf("create socket error!\n"); exit(1); } /* 2.绑定地址 */ bzero(&server_addr, sizeof(struct sockaddr_in)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORTNUM); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)); /* 3.监听端口 */ listen(sockfd, 5); while(1) { addr_len = sizeof(struct sockaddr); /* 4.等待连接 */ app_sockfd = accept(sockfd, (struct sockaddr *)(&client_addr), (socklen_t *)(&addr_len)); printf("server get connection from:%s\n", inet_ntoa(client_addr.sin_addr)); /* 5.接收数据 */ recv_sz = recv(app_sockfd, buff, 128, 0); buffer[recv_sz] = '\0'; printf("server received %s\n", buff); /* 6.结束连接 */ close(app_sockfd); } /* 7.结束连接 */ close(sockfd); return 0; } /* touch client.c */
/* 利用TCP协议进行网络通讯的客户端程序 */
#include <stdio.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #define PORTNUM 3333 int main() { int sockfd; int send_sz; char buff[128]; struct sockaddr_in server_addr; /* 1.创建socket */ if(-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0))) { printf("create socket error!\n"); exit(1); } /* 2.连接服务器 */ bzero(&server_addr, sizeof(struct sockaddr_in)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORTNUM); server_addr.sin_addr.s_addr = inet_addr("192.168.123.4"); if(-1 == connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) { printf("connet fail!\n"); exit(1); } /* 4.发送数据 */ printf("Please input something:\n"); fgets(buff, 128, stdin); send(sockfd, buffe, strlen(buffer), 0); /* 5.结束连接 */ close(sockfd); return 0; }