Linux网络编程之基础API使用
在本地进程中,我们可以通过一个进程号(PID)来标记唯一进程,那么在网络中,很明显该方法不可以。其实TCP/IP协议栈早早地帮助我们解决了该问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了。
在我们平常每天使用浏览器浏览网页,使用微信、QQ等工具来聊天时,所有的底层通信细节都得通过socket来实现。而socket,最开始的含义也就是一个IP地址和端口号,唯一表示了使用TCP的一端。
下面对常用的socket网络基础知识进行介绍。
1. SOCKET编程相关基础
1.1 主机字节序和网络字节序
大端字节序是指一个整数的高位字节存储在内存低地址处,地位字节存储在内存的高地址处。
小端字节序刚好相反。
网络传输为大端模式,多数PC机为小端模式。
主机字节序和网络字节序的转换:
#include <netinet/in.h>
unsigned short int htons()
unsigned long int htonl()
unsigned long int ntohl()
unsigned short int ntohs()
即使在同一机器上的两个进程通信,也要考虑字节序问题(JAVA虚拟机采用大端字节序)。
1.2 socket结构体定义
struct sockaddr_in
{
sa_family_t sin_family; //AF_INET
u_int16_t sin_port; //端口号,网络字节序
struct in_addr sin_addr;
};
struct in_addr
{
u_int32_t s_addr; //网络字节序
};
IP地址转换
#include <arpa/inet.h>
in_addr_t inet_addr(const char *strptr);
int inet_aton(const char *cp, struct in_addr *inp);
int inet_pton(int family, const char *src, void *dst);
char *inet_ntoa(struct in_addr in);
const char *inet_ntop(int family, const void *src, char *dst, size_t len);
注:inet_ntoa该函数内部由一个静态变量存储转化结果,不可重入。
inet_addr,不能处理广播地址255.255.255.255,被废弃的函数。用inet_aton或inet_pton代替。
2. 创建SOCKET
#include <sys/type.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
失败时返回-1。
type取值:
SOCKSTREAM SOCKDGRAM
SOCKRAW SOCKSEQPACKET
protocol取值:
IPPROTOTCP IPPROTOUDP
IPPROTO_SCTP
3. 命名SOCKET
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);//成功返回0,失败返回-1
4. 监听SOCKET
创建socket之后,还不能马上接受客户端连接,创建一个监听队列来存放待处理的客户连接。
int listen(int sockfd, int backlog);
backlog参数置顶内核监听队列的最大长度,典型值为5.
5. 接收连接
从listen监听队列中取出一个连接。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept只是从队列中取出连接,而不论连接处于何种状态,更不关心网络状况的变化。
6. 发起连接
客户端通过connect发起连接。
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
常见errno错误:
ECONNREFUSED,目标端口不存在,连接被拒绝。
ETIMEDOUT,连接超时
7. 关闭连接
int close(int fd);
int shutdown(int sockfd, int howto);
close() 系统调用并非总是立即关闭一个连接,而是讲fd引用计数减1,当引用计数为0时,才真正关闭连接。
立即终止连接,应使用shutdown()。
8. 数据读写
8.1 TCP数据读写
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
recv成功时返回实际读取到的数据长度,可能小于期望的长度,因此可能要多次recv,才可读取到完整数据。
8.2 UDP数据读写
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dst_addr, socklen_t addrlen);
9. 举个栗子
一个简单的TCP通信实例如下所示(还是直接截屏吧,不支持代码高亮,markdown确实是硬伤):
server端:
socket_server.c
client端:
socket_client.c
- 上一篇:没有了
- 下一篇:没有了