网络编程入门-实现服务器与客户端通讯
概念学习TCP概念TCPTransmission Control Protocol协议指的是传输控制协议是一个面向连接的传输协议他是一个能提供高可靠性的通信协议所谓高可靠性指的是数据无丢失、数据无误、数据无失序、数据无重到达。打电话适用场景适用于对传输质量要求较高以及传输大量数据的通信。在需要传输可靠数据的场合通常会选择使用TCP通信协议。比如QQ/微信/支付宝等通信软件的账户登录和支付相关功能是通常采用可靠的TCP通信协议来实现。UDP概念UDPUser Datagram Protocol指的是用户数据报协议是一种不可靠无连接的协议在数据发送前不需要提前建立连接所以可以更高效地传输数据。发邮件适用场景发送小尺寸地数据例如对DNS服务器进行地址查询或路游器更新路由表在收到数据给出应答比较困难地网络中适用UDP比如无线网络适用于广播/组播式通信。QQ/微信等即时通信软件地点对点文件通讯以及音视频通话时。流媒体、VoIOP、IPTV等网络多媒体服务中直播间相关API函数字节序转换API1 头文件 2 #include arpa/inet.h 3 4 函数原型 5 uint32_t htonl(uint32_t hostlong); // 主机字节序到网络字节序 无符号长整型 6 7 uint16_t htons(uint16_t hostshort);// 主机字节序到网络字节序 无符号短整型 8 9 uint32_t ntohl(uint32_t netlong); // 网络字节序转为主机字节序 无符号长整型 10 11 uint16_t ntohs(uint16_t netshort); // 网络字节序转为主机字节序 无符号短整型IP地址转换1 //头文件 2 #include sys/socket.h 3 #include netinet/in.h 4 #include arpa/inet.h 5 6 函数原型 7 // 把cp指向的字符串转换为32位的网络字节序的二进制值存于inp中 8 // cp 点分十进制的网络地址的字符串 IP地址 9 int inet_aton(const char *cp, struct in_addr *inp); 10 11 // 把cp指向的字符串转换为32位的网络字节序的二进制值并返回 12 in_addr_t inet_addr(const char *cp); 13 in_addr_t inet_network(const char *cp); 14 15 // 把in中的32位网络字节序的二进制地址转换为点分十进制的字符串 16 char *inet_ntoa(struct in_addr in);UDP相关API创建一个待连接套接字socket1 #include sys/socket.h 2 int socket(int domain, int type, int protocol); 3 参数 4 domain域。 5 AF_INET/PF_INET 网际协议 6 AF_UNIX/PF_UNIX本地协议可写成 AF_LOCAL/PF_LOCAL 7 type类型。 8 SOCK_STREAM流式套接字 TCP协议 9 SOCK_DGRAM数据报套接字 UDP协议 10 protocol协议。 11 一般为 0 12 返回值 13 成功待连接套接字 14 失败-1绑定地址#include sys/types.h #include sys/socket.h 1 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 2 参数 3 sockfd待连接套接字 4 addr包含本地地址IPPORT的通用地址结构体的指针 5 addrlen地址结构体大小 6 返回值 7 成功0 8 失败-1地址结构体我们要么用IPV4或IPV6因为直接使用通用结构体来配置不方便再将IPV4或IPV6的结构体强转为通用结构体。需要头文件#include arpa/inet.hstruct sockaddr // 通用IP信息结构体 { sa_family_t sa_family; char sa_data[14]; } struct sockaddr_in // IPV4地址结构体 { u_short sin_family;// 地址族网际协议/本地协议 u_short sin_port;// 端口65000 以上 struct in_addr sin_addr;// IPV4 地址ifconfig可以查看 char sin_zero[8];//暂时不管不知道干嘛的 }; struct in_addr // IP地址结构体 { in_addr_t s_addr;// 无符号 32 位网络地址 };发送数据到UDP1 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, 2 const struct sockaddr *dest_addr, socklen_t addrlen); 3 参数 4 sockfdUDP 套接字 5 buf即将发送的数据 6 len数据的长度 7 flags发送标志与函数 send 的 flags 完全一致 8 dest_addr对端网络地址 9 addr_len地址长度 10 返回值 11 成功已发送字节数 12 失败-1从UDP中接收数据1 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 2 struct sockaddr *src_addr, socklen_t *addrlen); 3 参数 4 sockfdUDP 套接字 5 buf储存数据缓冲区 6 len缓冲区大小 7 flags接收标志与函数 send 的 flags 完全一致 8 src_addr对端网络地址 9 addrlen地址长度 10 返回值 11 成功已接收字节数 12 失败返回-1UDP通信过程程序实现UDP通讯客户端向服务器发送数据服务端服务端实现思路就好比寄邮件第一步我们需要一个放邮件的邮箱创建套接字,socket()函数第二步我们要标注将这个邮件发往什么地方用IPV4地址结构体标注这个套接字的去向信息比如IP地址、端口号、那种协议第三步将邮件和标注信息绑定起来bind()函数第四部将邮件进行发送 recvfrom()函数#include stdio.h #include string.h #include stdlib.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include unistd.h #include arpa/inet.h //这个头文件包含了地址结构体的声明 #include netinet/in.h int main() { //1、创建信箱(套接字) //int socket(int domain, int type, int protocol); /* 函数头文件 #include sys/types.h #include sys/socket.h 参数1domain域。 AF_INET/PF_INET 网际协议 AF_UNIX/PF_UNIX本地协议可写成 AF_LOCAL/PF_LOCAL 参数2type type类型。 SOCK_STREAM流式套接字 TCP协议 SOCK_DGRAM数据报套接字 UDP协议 参数3protocol 设置为0 */ int socket_fd socket(AF_INET, SOCK_DGRAM, 0); if(-1 socket_fd) { perror(socket err); return -1; } //2、设置信箱地址和端口号 //设置信箱地址 /* 要使用该结构体必须包含头文件#include arpa/inet.h struct sockaddr_in // IPV4地址结构体 { u_short sin_family;// 地址族网际协议/本地协议 u_short sin_port;// 端口65000 以上 struct in_addr sin_addr;// IPV4 地址ifconfig可以查看 char sin_zero[8];//暂时不管不知道干嘛的 }; */ struct sockaddr_in my_sockaddr {0}; my_sockaddr.sin_family AF_INET;//网际协议 IPV4 my_sockaddr.sin_port htons(65000);//用函数将主机字节序转换为网络字节序才能进行茶传输 //in_addr_t inet_addr(const char *cp); my_sockaddr.sin_addr.s_addr inet_addr(192.168.81.1); int len sizeof(struct sockaddr_in); //3.把设置好的信息与信箱进行绑定 // int bind(int sockfd, const struct sockaddr *addr, // socklen_t addrlen); /* 头文件 #include sys/types.h #include sys/socket.h 参数说明 sockfd待连接套接字 addr信箱地址 addrlen 信箱大小 */ int ret_val bind(socket_fd, (struct sockaddr *)my_sockaddr, len); if(-1 ret_val) { perror(bind error); return -1; } //4.等待客户端来信 //先让一直等待接受 char * msg calloc(128,1);//存储接收到的信息 struct sockaddr_in src_addr;//将接收到的套接字存放在此处 while(1) { /* 头文件 #include sys/types.h #include sys/socket.h 函数 ssize_t recv(int sockfd, void *buf, size_t len, int flags); 函数参数说明 sockfd套接字 buf存储接收到的地址 len接收到数据的长度 flags与socket函数第三个参数保持一致 缺点用这个函数接收不知道是谁发的 使用下面这个 函数 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 函数参数说明src_addr发送方的邮箱地址 addrlen 发送方邮箱地址长度 */ ssize_t ret_val recvfrom(socket_fd, msg, 128, 0, (struct sockaddr *)src_addr, len); if(-1 ret_val) { perror(recvfrom error); continue; } else { printf(recvfrom succeed,msg:%s\n,msg); //如果需要打印别的信息src_addr这个结构体里将数据进行转换显示 // char *inet_ntoa(struct in_addr in); 把in中的32位网络字节序的二进制地址转换为点分十进制的字符串 printf(addr:%s\n,inet_ntoa( src_addr.sin_addr ));//显示发送者的IP地址 } } return 0; }客户端客户端不要将信件和信件的去向信息进行绑定客户端是发送信息#include stdio.h #include string.h #include stdlib.h #include sys/types.h /* See NOTES */ #include sys/socket.h #include unistd.h #include arpa/inet.h //这个头文件包含了地址结构体的声明 #include netinet/in.h int main() { //1、创建信箱(套接字) //int socket(int domain, int type, int protocol); /* 函数头文件 #include sys/types.h #include sys/socket.h 参数1domain域。 AF_INET/PF_INET 网际协议 AF_UNIX/PF_UNIX本地协议可写成 AF_LOCAL/PF_LOCAL 参数2type type类型。 SOCK_STREAM流式套接字 TCP协议 SOCK_DGRAM数据报套接字 UDP协议 参数3protocol 设置为0 */ int socket_fd socket(AF_INET, SOCK_DGRAM, 0); if(-1 socket_fd) { perror(socket err); return -1; } //2、设置信箱地址和端口号 //设置信箱地址 /* 要使用该结构体必须包含头文件#include arpa/inet.h struct sockaddr_in // IPV4地址结构体 { u_short sin_family;// 地址族网际协议/本地协议 u_short sin_port;// 端口65000 以上 struct in_addr sin_addr;// IPV4 地址ifconfig可以查看 char sin_zero[8];//暂时不管不知道干嘛的 }; */ struct sockaddr_in my_sockaddr {0}; my_sockaddr.sin_family AF_INET;//网际协议 IPV4 my_sockaddr.sin_port htons(65000);//用函数将主机字节序转换为网络字节序才能进行茶传输 //in_addr_t inet_addr(const char *cp); my_sockaddr.sin_addr.s_addr inet_addr(192.168.81.1); int len sizeof(struct sockaddr_in); //4.等待客户端来信 //先让一直等待接受 char * msg calloc(128,1);//存储接收到的信息 struct sockaddr_in src_addr;//将接收到的套接字存放在此处 while(1) { /* 头文件 #include stdio.h 函数 char *fgets(char *s, int size, FILE *stream); s:从哪里获取信息 size:信息长度 stream文件流获取 */ printf(发送的消息为\n); fgets(msg,128,stdin); /* 头文件 #include sys/types.h #include sys/socket.h 函数 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); 参数说明sockfd 发送的邮箱套接字 buf发送内容地址 len发送消息长度 flags:与服务器接受标志一致 dest_addr发送目标邮箱地址 addrlen发送目标邮箱地址长度 */ int ret_val -1; ret_val sendto(socket_fd, msg, strlen(msg), 0, (struct sockaddr *)my_sockaddr, len); if(ret_val -1) { printf(发送错误); } else { printf(发送内容为%s\n,msg); } } return 0; }实验结果两个端口之间可以进行通讯服务器和客户端互相通讯服务器想要发送消息到客户端就需要写发送功能1、服务器想要发送消息给用户端首先用户端必须先连接上服务器2、当连接上之后服务器就有了用户端的IP地址就可以将信息进行发送。