网络编程day02
- 游戏开发
 - 2025-09-11 11:15:01
 

网络编程 day02 6. 字节序端口转换IP转换 7. TCP编程1. 流程2. 函数接口socketbindlistenacceptrecvconnectsend 4. 通信代码优化过程5. 最终通信代码 8. 粘包 6. 字节序 端口转换
主机字节序转换为网络字节序 (小端序->大端序)
#include <arpa/inet.h> u_long htonl (u_long hostlong); // host to net long u_short htons (u_short short); // host to net short网络字节序转换为主机字节序(大端序->小端序)
u_long ntohl (u_long hostlong); u_short ntohs (u_short short); IP转换主机字节序转换为网络字节序 (小端序->大端序)
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> in_addr_t inet_addr(const char *strptr); //该参数是字符串 typedef uint32_t in_addr_t;功能: 主机字节序转为网络字节序 参数: const char *strptr: 字符串 返回值: 返回一个无符号长整型数(无符号32位整数用十六进制表示), 否则NULL
网络字节序转换为主机字节序(大端序->小端序)
char *inet_ntoa(stuct in_addr inaddr);功能: 将网络字节序二进制地址转换成主机字节序。 参数: stuct in_addr in addr: 只需传入一个结构体变量 返回值: 返回一个字符指针, 否则NULL; 注意:
struct in_addr { in_addr_t s_addr; }; 7. TCP编程C/S:client/server 客户端与服务器 B/S:browser/server 浏览器与服务器
1. 流程服务器
创建套接字——socket()指定网络信息绑定套接字——bind()监听套接字——listen()接收客户端连接请求——accept()创建新的套接字——socket()接收/发送消息——recv()/send()关闭套接字——close()客户端
创建套接字——socket()指定服务器网络信息连接请求——connect()接收/发送消息——recv()/send()关闭套接字——close() 2. 函数接口 socket #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);功能:创建套接字 参数: int domain协议族 int type套接字类型 int protocol协议 返回值:成功返回套接字文件描述符,失败返回-1,更新errno 补充:
int domain协议族 宏名含义AF_UNIX, AF_LOCAL本地通信AF_INETipv4AF_INET6ipv6 int type套接字类型 宏名含义SOCK_STREAM流式套接字SOCK_DGRAM数据报套接字SOCK_RAW原始套接字 int protocol协议:0 自动匹配底层 bind #include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能:绑定套接字 参数: int sockfd:套接字 const struct sockaddr *addr:用于通信结构体,需要强转 socklen_t addrlen:结构体大小 返回值:成返回0,失败返回-1,更新errno 补充:
struct sockaddr_in { sa_family_t sin_family; // ipv4协议 in_port_t sin_port; // 端口号 struct in_addr sin_addr; // IP地址 }; struct in_addr { uint32_t s_addr; // IP地址 }; listen #include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog);功能:监听套接字,将主动套接字变为被动套接字 参数: int sockfd:连接套接字的文件描述符 int backlog:同时响应客户端请求链接的最大个数 返回值:成功 0 失败-1,更新errno 补充: 参数:int backlog:不能写0,一般写6 不同平台可同时链接的数不同 两个队列: 队列1:保存正在连接 队列2,连接上的客户端
accept #include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);功能:阻塞等待客户端的连接请求 参数: int sockfd:链接套接字 struct sockaddr *addr: 客户端网络信息结构体,需要强转 socklen_t *addrlen:客户端网络信息结构体的大小 返回值: 成功:通信套接字的文件描述符,失败:-1,更新errno 补充:
如果不需要关心具体是哪一个客户端,那么可以填NULL获取的ip和端口号需要转换大小端 recv #include <sys/types.h> #include <sys/socket.h> ssize_t recv(int sockfd, void *buf, size_t len, int flags);功能:接收数据 参数: int sockfd:用于通信的套接字文件描述符 void *buf:存放通信内容 size_t len: 获取的内容的大小 int flags 一般填0,等价于read MSG_DONTWAIT 非阻塞 返回值: 成功:接收的字节个数,为0时表示客户端退出 失败:< 0,更新errno
connect #include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);功能:用于连接服务器 参数: int sockfd:通信用的套接字 const struct sockaddr *addr:服务器网络信息 socklen_t addrlen:结构体的大小 返回值:成功返回0,失败返回-1,更新errno
send #include <sys/types.h> #include <sys/socket.h> ssize_t send(int sockfd, const void *buf, size_t len, int flags);功能:发送数据 参数: int sockfd通信用的套接字 const void *buf存放通信内容 size_t len发送的内容的大小 int flags一般填0,等价于write
4. 通信代码优化过程 优化服务器代码,客户端链接成功后,可以循环多次通信,当客户端输入quit时,客户端退出。优化服务器代码客户端输入quit退出后,服务器不退出,等待下一个客户端连接 循环服务器:一个服务器可以连接多个客户端,但是不能同时地址和端口都通过参数传入自动获取本机地址增加来电显示功能:显示客户端连入的地址和端口 5. 最终通信代码服务器
int main(int argc, char const *argv[]) { // 变量定义 int fd_socket = -1; // 连接套接字文件描述符 struct sockaddr_in saddr; // 服务器网络信息 struct sockaddr_in caddr; // 客户端网络信息 int acc = -1; // 通信套接字的文件描述符 int len = sizeof(caddr); // 客户端网络信息结构体大小 char buf[128] = {}; // 存放收发的内容 int ret = -1; // 返回值 // 1. 创建套接字 fd_socket = socket(AF_INET, SOCK_STREAM, 0); if (fd_socket < 0) { perror("socket err"); return -1; } printf("fd_socket : %d\n", fd_socket); // 2. 指定网络信息 saddr.sin_family = AF_INET; saddr.sin_port = htons(atoi(argv[1])); saddr.sin_addr.s_addr = INADDR_ANY; // 3. 绑定套接字 if (bind(fd_socket, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { perror("bind err"); return -1; } printf("bind ok\n"); // 4. 监听套接字 // 将主动套接字变成被动套接字 // 队列1:未连接 // 队列2:已连接 if (listen(fd_socket, 6) < 0) { perror("listen err"); return -1; } printf("listen ok\n"); while (1) { // 接收客户端连接请求 acc = accept(fd_socket, (struct sockaddr *)&caddr, &len); if (acc < 0) { perror("accept err"); return -1; } printf("accfd : %d\n", acc); printf("ip:%s\tport:%d\t", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port)); while (1) { // 接收消息 ret = recv(acc, buf, sizeof(buf), 0); if (ret < 0) { perror("recv err"); return -1; } else if (ret == 0) { printf("client close\n"); break; } // 使用通信信息 printf("buf : %s\n", buf); memset(buf, 0, sizeof(buf)); } } // 关闭文件描述符 close(acc); close(fd_socket); return 0; }客户端
int main(int argc, char const *argv[]) { int sockfd = -1; // 套接字文件描述符 char buf[128] = {}; // 通信内容 int ret = 0; // 返回值 // 创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket err"); return -1; } printf("sockfd : %d\n", sockfd); // 指定服务器网络信息 struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(atoi(argv[2])); saddr.sin_addr.s_addr = inet_addr(argv[1]); // 连接请求 ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret) { perror("connect err"); return -1; } printf("connect ok\n"); // 发送消息 while (1) { fgets(buf, sizeof(buf), stdin); if(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; if(!strcmp(buf, "quit")) { printf("client close\n"); break; } send(sockfd, buf, strlen(buf), 0); } // 关闭文件描述符 close(sockfd); return 0; } 8. 粘包