Linux网络编程

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‬。

sockaddr数据结构

早期的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。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值