linux网络编程平台(Linux网络编程)
linux网络编程平台(Linux网络编程)有4个函数专门用于转换:大写V,ASCII值为86,对应16进制为56。可以看出电脑上是小端法存储。4字节整数a的存储示意如图所示:写个小程序,把a的最低位输出来,看看是多少。#include<stdio.h> int main(int argc char const *argv[]) { printf("%d\n" sizeof(int) ); unsigned int a=0x12345678; printf("%c\n" a); printf("%c\n" *((char*)&a 1)); printf("%c\n" *((char*)&a 2)); printf("%c\n" *((char*)&a 3)); return 0; }上面程序输出为x,小写x的ASCII值
Socket一个文件描述符指向一个套接字(套接字内部由内核借助两个缓冲区实现)
小端法(PC):高位存高地址,地位存地址
大端法(网络):高位存低地址,地位存高地址
TCP/IP协议规定,网络数据流采用大端字节序;而主机当中使用的是小端法,需要做网络字节序和主机字节序的转换。
4字节整数a的存储示意如图所示:
写个小程序,把a的最低位输出来,看看是多少。
#include<stdio.h>
int main(int argc char const *argv[])
{
printf("%d\n" sizeof(int) );
unsigned int a=0x12345678;
printf("%c\n" a);
printf("%c\n" *((char*)&a 1));
printf("%c\n" *((char*)&a 2));
printf("%c\n" *((char*)&a 3));
return 0;
}
上面程序输出为x,小写x的ASCII值为120,而0x78转换为10进制就是120。
大写V,ASCII值为86,对应16进制为56。可以看出电脑上是小端法存储。
有4个函数专门用于转换:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示host,n表示network,l表示32位,s表示16位。
htonl:本地->网络(IP)
htons:本地->网络(port)
ntohl:网络->本地(IP)
ntohs:网络->本地(port)
IP地址转换函数inet_pton:点分十进制字符串转换为网络字节序
inet_ntop:网络字节序转换为点分十进制字符串
这两个函数都支持IPv4和IPv6.
#include <arpa/inet.h>
int inet_pton(int af const char *src void *dst);
af:表示地址类型,只有两个选择,AF_INET表示IPv4,AF_INET6表示IPv6
src:要转换的IP(点分十进制字符串)
det:转换后的网络字节序的IP地址
返回值:
成功:1
异常:0,表示scr指向的不是一个有效的IP地址
失败:-1
#include <arpa/inet.h>
const char *inet_ntop(int af const void *src char *dst socklen_t size)
af:AF_INET、AF_INET6
scr:网络字节序的IP地址
dst:本地字节序(string IP)
size:dst的大小
返回值:
成功:dst
失败:NULL;
https://blog.csdn.net/bian_qing_quan11/article/details/71699371
inet_ntoa:网络字节序转换为点分十进制IP
char *inet_ntoa(struct in_addr in);
inet_aton:点分十进制IP转换为网络字节序存放在addr中,成功返回1,失败返回0。
inet_aton() returns 1 if the supplied string was successfully interpreted or 0 if the string is invalid (errno is not set on
error).
int inet_aton(const char *cp struct in_addr *inp);
inet_network:将点分十进制IP转化为主机字节序(二进制位小端存储)
in_addr_t inet_network(const char *cp);
inet_addr:将点分十进制IP转化为网络字节序(二进制位的大端存储)。
in_addr_t inet_addr(const char *cp);
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc char const *argv[])
{
struct in_addr in;
printf("点分十进制ip地址:192.168.1.1\n");
printf("主机字节序:%u\n" inet_network("192.168.1.1"));
inet_aton("192.168.1.1" &in);
printf("网络字节序:%u\n" in.s_addr);
in.s_addr = htonl(inet_network("192.168.1.1"));
printf("点分十进制ip地址:%s\n" inet_ntoa(in));
return 0;
}
192.168.1.1转换成二进制为11000000 10101000 00000001 00000001,转换为十进制为3 232 235 777,大端存储为00000001 00000001 10101000 11000000,即16 885 952。
早期的socket数据结构,sockaddr数据结构其实已经不用了,但因为Linux当中很多函数以前用的这个数据结构,不好对这些函数进行更改,就保留了sockaddr数据结构,它就扮演着void *差不多的角色,用作地址转换中介。例如bind的函数当中参数类型还是sockaddr*类型,使用时需要进行地址类型转换。
int bind(int sockfd const struct sockaddr *addr socklen_t addrlen);
sturct sockaddr_in addr; man 7 ip查看sockaddr_in结构体信息。
addr.sin_family=AF_INT; (sin,socket internet??)
addr.sin_port=htons(9527);
//int dst;
//inet_pton(AR_INET "11.11.11.11" (void*)&dst);
//add.sin_addr.s_addr=dst;
addr.sin_addr.s_addr=htonl(INADDR_ANY);取出系统中有效的任意IP地址,二进制类型。INADDR_ANY是一个宏。
bind(fd (struct sockaddr*)&addr size);
struct sockaddr_in:这个in表示internet,不是进入哈哈(我前两天一直以为是输入,很懵逼)
man 7 ip查看sockaddr_in数据结构
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
sockaddr_un是本地进程通信的数据结构。
socket模型创建流程
socket():创建一个套接字,返回一个文件描述符fd,也叫句柄。
bind():绑定IP 端口
listen():设置监听上线,表示同时能连接的客户端数量
accept():阻塞监听客户端连接
connet(),绑定IP和端口
socket函数socket()
#include <sys/socket.h>
int socket(int domain int type int protocol);
domain:通信协议,AF_INET、AF_INET6、AF_UNIX、AF_NETLINK等
type:数据传输方式,有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等
protocol:对应协议,当protocol为0时,会自动选择type类型对应的默认协议
正如大家所想,一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。
使用 IPv4 地址,参数 af 的值为 PF_INET。如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP,因此可以这样来调用 socket() 函数:
int tcp_socket = socket(AF_INET SOCK_STREAM IPPROTO_TCP); //IPPROTO_TCP表示TCP协议
返回值:成功返回文件描述符,错误返回-1。
/usr/include/x86_64-linux-gnu/bits/socket.h
/* Protocol families. */
#define PF_UNSPEC 0 /* Unspecified. */
#define PF_LOCAL 1 /* Local to host (pipes and file-domain). */
#define PF_UNIX PF_LOCAL /* POSIX name for PF_LOCAL. */
#define PF_FILE PF_LOCAL /* Another non-standard name for PF_LOCAL. */
#define PF_INET 2 /* IP protocol family. */
#define PF_AX25 3 /* Amateur Radio AX.25. */
#define PF_IPX 4 /* Novell Internet Protocol. */
#define PF_APPLETALK 5 /* Appletalk DDP. */
#define PF_NETROM 6 /* Amateur radio NetROM. */
#define PF_BRIDGE 7 /* Multiprotocol bridge. */
#define PF_ATMPVC 8 /* ATM PVCs. */
#define PF_X25 9 /* Reserved for X.25 project. */
#define PF_INET6 10 /* IP version 6. */
#define PF_ROSE 11 /* Amateur Radio X.25 PLP. */
#define PF_DECnet 12 /* Reserved for DECnet project. */
#define PF_NETBEUI 13 /* Reserved for 802.2LLC project. */
#define PF_SECURITY 14 /* Security callback pseudo AF. */
#define PF_KEY 15 /* PF_KEY key management API. */
#define PF_NETLINK 16
#define PF_ROUTE PF_NETLINK /* Alias to emulate 4.4BSD. */
#define PF_PACKET 17 /* Packet family. */
#define PF_ASH 18 /* Ash. */
#define PF_ECONET 19 /* Acorn Econet. */
#define PF_ATMSVC 20 /* ATM SVCs. */
#define PF_RDS 21 /* RDS sockets. */
#define PF_SNA 22 /* Linux SNA Project */
#define PF_IRDA 23 /* IRDA sockets. */
#define PF_PPPOX 24 /* PPPoX sockets. */
#define PF_WANPIPE 25 /* Wanpipe API sockets. */
#define PF_LLC 26 /* Linux LLC. */
#define PF_IB 27 /* Native InfiniBand address. */
#define PF_MPLS 28 /* MPLS. */
#define PF_CAN 29 /* Controller Area Network. */
#define PF_TIPC 30 /* TIPC sockets. */
#define PF_BLUETOOTH 31 /* Bluetooth sockets. */
#define PF_IUCV 32 /* IUCV sockets. */
#define PF_RXRPC 33 /* RxRPC sockets. */
#define PF_ISDN 34 /* mISDN sockets. */
#define PF_PHONET 35 /* Phonet sockets. */
#define PF_IEEE802154 36 /* IEEE 802.15.4 sockets. */
#define PF_CAIF 37 /* CAIF sockets. */
#define PF_ALG 38 /* Algorithm sockets. */
#define PF_NFC 39 /* NFC sockets. */
#define PF_VSOCK 40 /* vSockets. */
#define PF_KCM 41 /* Kernel Connection Multiplexor. */
#define PF_QIPCRTR 42 /* Qualcomm IPC Router. */
#define PF_SMC 43 /* SMC sockets. */
#define PF_MAX 44 /* For now.. */
/* Address families. */
#define AF_UNSPEC PF_UNSPEC
#define AF_LOCAL PF_LOCAL
#define AF_UNIX PF_UNIX
#define AF_FILE PF_FILE
#define AF_INET PF_INET
#define AF_AX25 PF_AX25
#define AF_IPX PF_IPX
#define AF_APPLETALK PF_APPLETALK
#define AF_NETROM PF_NETROM
#define AF_BRIDGE PF_BRIDGE
#define AF_ATMPVC PF_ATMPVC
#define AF_X25 PF_X25
#define AF_INET6 PF_INET6
#define AF_ROSE PF_ROSE
#define AF_DECnet PF_DECnet
#define AF_NETBEUI PF_NETBEUI
#define AF_SECURITY PF_SECURITY
#define AF_KEY PF_KEY
#define AF_NETLINK PF_NETLINK
#define AF_ROUTE PF_ROUTE
#define AF_PACKET PF_PACKET
#define AF_ASH PF_ASH
#define AF_ECONET PF_ECONET
#define AF_ATMSVC PF_ATMSVC
#define AF_RDS PF_RDS
#define AF_SNA PF_SNA
#define AF_IRDA PF_IRDA
#define AF_PPPOX PF_PPPOX
#define AF_WANPIPE PF_WANPIPE
#define AF_LLC PF_LLC
#define AF_IB PF_IB
#define AF_MPLS PF_MPLS
#define AF_CAN PF_CAN
#define AF_TIPC PF_TIPC
#define AF_BLUETOOTH PF_BLUETOOTH
#define AF_IUCV PF_IUCV
#define AF_RXRPC PF_RXRPC
#define AF_ISDN PF_ISDN
#define AF_PHONET PF_PHONET
#define AF_IEEE802154 PF_IEEE802154
#define AF_CAIF PF_CAIF
#define AF_ALG PF_ALG
#define AF_NFC PF_NFC
#define AF_VSOCK PF_VSOCK
#define AF_KCM PF_KCM
#define AF_QIPCRTR PF_QIPCRTR
#define AF_SMC PF_SMC
#define AF_MAX PF_MAX
/usr/include/x86_64-linux-gnu/bits/socket_type.h
/* Types of sockets. */
enum __socket_type
{
SOCK_STREAM = 1 /* Sequenced reliable connection-based
byte streams. */
#define SOCK_STREAM SOCK_STREAM
SOCK_DGRAM = 2 /* Connectionless unreliable datagrams
of fixed maximum length. */
#define SOCK_DGRAM SOCK_DGRAM
SOCK_RAW = 3 /* Raw protocol interface. */
#define SOCK_RAW SOCK_RAW
SOCK_RDM = 4 /* Reliably-delivered messages. */
#define SOCK_RDM SOCK_RDM
SOCK_SEQPACKET = 5 /* Sequenced reliable connection-based
datagrams of fixed maximum length. */
#define SOCK_SEQPACKET SOCK_SEQPACKET
SOCK_DCCP = 6 /* Datagram Congestion Control Protocol. */
#define SOCK_DCCP SOCK_DCCP
SOCK_PACKET = 10 /* Linux specific way of getting packets
at the dev level. For writing rarp and
other similar things on the user level. */
#define SOCK_PACKET SOCK_PACKET
/* Flags to be ORed into the type parameter of socket and socketpair and
used for the flags parameter of paccept. */
SOCK_CLOEXEC = 02000000 /* Atomically set close-on-exec flag for the
new descriptor(s). */
#define SOCK_CLOEXEC SOCK_CLOEXEC
SOCK_NONBLOCK = 00004000 /* Atomically mark descriptor(s) as
non-blocking. */
#define SOCK_NONBLOCK SOCK_NONBLOCK
};
/usr/include/linux/in.h
#if __UAPI_DEF_IN_IPPROTO
/* Standard well-defined IP protocols. */
enum {
IPPROTO_IP = 0 /* Dummy protocol for TCP */
#define IPPROTO_IP IPPROTO_IP
IPPROTO_ICMP = 1 /* Internet Control Message Protocol */
#define IPPROTO_ICMP IPPROTO_ICMP
IPPROTO_IGMP = 2 /* Internet Group Management Protocol */
#define IPPROTO_IGMP IPPROTO_IGMP
IPPROTO_IPIP = 4 /* IPIP tunnels (older KA9Q tunnels use 94) */
#define IPPROTO_IPIP IPPROTO_IPIP
IPPROTO_TCP = 6 /* Transmission Control Protocol */
#define IPPROTO_TCP IPPROTO_TCP
IPPROTO_EGP = 8 /* Exterior Gateway Protocol */
#define IPPROTO_EGP IPPROTO_EGP
IPPROTO_PUP = 12 /* PUP protocol */
#define IPPROTO_PUP IPPROTO_PUP
IPPROTO_UDP = 17 /* User Datagram Protocol */
#define IPPROTO_UDP IPPROTO_UDP
IPPROTO_IDP = 22 /* XNS IDP protocol */
#define IPPROTO_IDP IPPROTO_IDP
IPPROTO_TP = 29 /* SO Transport Protocol Class 4 */
#define IPPROTO_TP IPPROTO_TP
IPPROTO_DCCP = 33 /* Datagram Congestion Control Protocol */
#define IPPROTO_DCCP IPPROTO_DCCP
IPPROTO_IPV6 = 41 /* IPv6-in-IPv4 tunnelling */
#define IPPROTO_IPV6 IPPROTO_IPV6
IPPROTO_RSVP = 46 /* RSVP Protocol */
#define IPPROTO_RSVP IPPROTO_RSVP
IPPROTO_GRE = 47 /* Cisco GRE tunnels (rfc 1701 1702) */
#define IPPROTO_GRE IPPROTO_GRE
IPPROTO_ESP = 50 /* Encapsulation Security Payload protocol */
#define IPPROTO_ESP IPPROTO_ESP
IPPROTO_AH = 51 /* Authentication Header protocol */
#define IPPROTO_AH IPPROTO_AH
IPPROTO_MTP = 92 /* Multicast Transport Protocol */
#define IPPROTO_MTP IPPROTO_MTP
IPPROTO_BEETPH = 94 /* IP option pseudo header for BEET */
#define IPPROTO_BEETPH IPPROTO_BEETPH
IPPROTO_ENCAP = 98 /* Encapsulation Header */
#define IPPROTO_ENCAP IPPROTO_ENCAP
IPPROTO_PIM = 103 /* Protocol Independent Multicast */
#define IPPROTO_PIM IPPROTO_PIM
IPPROTO_COMP = 108 /* Compression Header Protocol */
#define IPPROTO_COMP IPPROTO_COMP
IPPROTO_SCTP = 132 /* Stream Control Transport Protocol */
#define IPPROTO_SCTP IPPROTO_SCTP
IPPROTO_UDPLITE = 136 /* UDP-Lite (RFC 3828) */
#define IPPROTO_UDPLITE IPPROTO_UDPLITE
IPPROTO_MPLS = 137 /* MPLS in IP (RFC 4023) */
#define IPPROTO_MPLS IPPROTO_MPLS
IPPROTO_RAW = 255 /* Raw IP packets */
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_MAX
};
#endif
bind()
int bind(int sockfd const struct sockaddr *addr socklen_t addrlen);
给socket绑定一个地址结构(IP 端口号),socket和sockaddr_in中的地址结构AF_INT等得一样。
sockfd:socket函数返回值,文件描述符
sturct sockaddr_in addr;
addr.sin_family=AF_INT;
addr.sin_port=htons(9527);
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr:(struct sockaddr&)&addr,是传入参数
addlen:sizeof(addr),地址结构的大小
返回值:
成功:0;失败:-1
listen()
int listen(int sockfd int backlog);
设置能够同时与服务器建立连接的客户端上线(同时进行3次握手的客户端数量)
sockfd:socket函数返回值,文件描述符
backlog:上限数值,最大为128。
返回值:
成功:0;失败:-1
accept()
int accept(int sockfd struct sockaddr *addr socklen_t *addrlen);
阻塞等待客户端建立连接,成功时返回一个与客户端成功连接的socket文件描述符
sockfd:最开始建立的socket文件描述符
addr:传出参数,成功与服务器建立连接的那个客户端的地址结构(IP port)
addrlen:传入传出参数。入:addr的大小,出:客户端addr的实际大小。
socklen_t clit_addr_len = sizeof(struct sockaddr),传入参数&clit_addr_len。
返回值:
成功,返回新建立的socket的文件描述符,非负整数
失败:-1
connect()
int connect(int sockfd const struct sockaddr *addr socklen_t addrlen);
与服务器建立连接
sockfd:socket函数返回值
addr:传入参数,服务器地址结构
addrlen:服务器地址结构长度
返回值:成功0,失败-1。
如果不使用bind绑定客户端地址结构,系统会“隐式绑定”。
一个简单的例子,客户端给服务器发一串字符串,服务器将接受到字符串转换为大写,再发功给客户端。
这里用的read、write函数,和recv、send区别后面再研究。
//server.c
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_PORT 12345
void sys_error(const char *str)
{
perror(str);
exit(-1);
}
int main(int argc char const *argv[])
{
int sfd cfd ret=0;
socklen_t client_addr_len;
char buf[1024];
char client_IP[16];
struct sockaddr_in server_addr client_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
sfd = socket(AF_INET SOCK_STREAM 0);
if(sfd==-1)
{
sys_error("socket error");
}
ret = bind(sfd (struct sockaddr*)&server_addr sizeof(server_addr));
if(ret==-1)
{
sys_error("bind error");
}
ret = listen(sfd 128);
if(ret==-1)
{
sys_error("listen error");
}
client_addr_len = sizeof(client_addr);
cfd = accept(sfd (struct sockaddr*)&client_addr &client_addr_len);
if(cfd==-1)
{
sys_error("accept error");
}
//打印连接上的客户端IP和端口号
printf("client_ip:%s port:%d\n" inet_ntop(AF_INET &client_addr.sin_addr.s_addr client_IP sizeof(client_IP)) ntohs(client_addr.sin_port) );
while(1)
{
ret = read(cfd buf sizeof(buf));
write(STDOUT_FILENO buf ret);
for(int i=0;i<ret;i )
{
buf[i] = toupper(buf[i]);
}
write(cfd buf ret);
}
close(sfd);
close(cfd);
return 0;
}
还没有写客户端,可以nc命令模拟客户端
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define SERVER_PORT 12347
void sys_error(const char *str)
{
perror(str);
exit(-1);
}
int main(int argc char const *argv[])
{
int cfd ret=0;
char buf[1024];
struct sockaddr_in client_addr server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
//server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
inet_pton(AF_INET "127.0.0.1" &server_addr.sin_addr.s_addr);
cfd = socket(AF_INET SOCK_STREAM 0);
if(cfd==-1)
{
sys_error("socket error");
}
ret = connect(cfd (struct sockaddr*)&server_addr sizeof(server_addr));
if(ret==-1)
{
sys_error("connect error");
}
while(1)
{
//scanf("%s" buf);
gets(buf);//最好使用fgets。
write(cfd buf strlen(buf));
ret = read(cfd buf sizeof(buf));
write(STDOUT_FILENO buf ret);
printf("\n");
}
close(cfd);
return 0;
}
TCP三次握手,对应的函数就是accept,connect。