一、网络基础知识
二、基于TCP的网络编程
二、基于TCP的网络编程
1、编程中用到的辅助函数
(1)主机字节序和网络字节序的转换
因为数据存储方式涉及大端和小端,不同方式通信时需要转换,网络字节序为大端;可以使用以下函数:
#include<arpa.inet.h>
uint32_t htonl(uint_32_t hostlong);
uint16_t htons(uint_16_t hostshort);
uint32_t ntohl(uint_32_t netlong);
uint16_t nonhs(uint_16_t hostshort);
其中:h代表host,n代表net,l代表long,s代表short
(2)具体的网络协议地址空间和通用的网络地址空间的转换。
具体的网络协议族有ipv4,ipv6,local,unix等,各自有各自的地址空间,通用地址空间的类型为 struct sockaddr类型,在bind,accept等函数中都要用此类型做参数,可使用以下函数做转换:
inet_pton(3)
int inet_pton(int af, const char *src, void *dst);
功能:
转换src(点分十进制的地址格式)指定的字符串为网络地址结构到af指定的网络地址家族(即text to binary),并拷贝网络地址结构到dst中,af必须指定为AF_INET或者AF_INET 6之一。
参数:
af:
AF_INET
AF_INET6
src:点分十进制的字符串IP地址
dst:转换后的内容,struct in_addr类型(根据af指定协议转换后格式不同,此处以AF_INET为例)
返回值:
成功:1
src不可用:0
af不可用:-1,errno被设置为EAFNO_SUPPORT
inet_ntop(3)
const char * inet_ntop(int af, const void *src, char *dst, socklen_t size); //将数值格式转化为点分十进制的ip地址格式
功能:
将在af指定的网络地址空间的src转换为字符串,即将ipv4或ipv6地址从二进制转换到文本结果被拷贝到dst(非空)指定的缓冲区中
参数:
af:
AF_INET
AF_INET6
src:
如果指定为AF_INET,则该类型为struct in_addr
dst:
字符串地址空间
size:
指定了buffer的有效字节数
返回值:
错误:NULL,errno被设置
成功:指向dst的空间的地址
(3)ipv4的sockaddr_in结构体
struct sockaddr_in{
sa_family_t sin_family AF_INET.
in_port_t sin_port Port number.
struct in_addr sin_addr IP address.
}
另外;struct in_addr至少要包含in_addr_t s_addr这个成员
struct sockaddr_in{
in_addr_t s_addr;//无符号32位,在inttypes.h中被定义
}
2、基于TCP的客户端编程
(1)使用socket(2)创建铜须描述符
(2)使用bind(2)绑定端口号与IP地址
(3)使用connect(2)链接服务器
(4)数据发送接收
(5)等待服务器相应
(6)关闭服务器连接
connect(2)返回代表客户端的三次握手结束,accept(2)的返回代表服务器的三次握手结束
connect(2)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
功能:
把sockfd指定的socket连接到addr指定的地址空间
参数:
sockfd:socket(2)的返回值
addr:要连接的服务器的地址空间,格式由sockfd决定
addrlen:addr的大小
返回值:
成功:0
失败:-1,errno被设置
返回:若成功则为0,若出错则为-1
基于TCP服务器代码:
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> int main() { int listenfd = socket (AF_INET, SOCK_STREAM, 0); if (listenfd == -1) { perror ("socket"); exit (EXIT_FAILURE); } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons (8888); addr.sin_addr.s_addr = INADDR_ANY; if (bind (listenfd, (struct sockaddr*)&addr, sizeof (addr)) == -1) { perror ("bind"); exit (EXIT_FAILURE); } if (listen (listenfd, 1024) == -1) { perror ("listen"); exit (EXIT_FAILURE); } struct sockaddr_in addrcli = {}; socklen_t addrlen = sizeof (addrcli); int connfd = accept (listenfd, (struct sockaddr*)&addrcli, &addrlen); if (connfd == -1) { perror ("accept"); exit (EXIT_FAILURE); } printf ("服务器已接受来自%s:%hu客户机的连接请求 ", inet_ntoa (addrcli.sin_addr),ntohs (addrcli.sin_port)); char buf[1024]; ssize_t rcvd = recv (connfd, buf, sizeof (buf), 0); if (rcvd == -1) { perror ("recv"); exit (EXIT_FAILURE); } if (rcvd == 0) { printf ("客户机已关闭连接 "); exit (EXIT_FAILURE); } buf[rcvd] = '