linux内核学习之网络篇——通过socket通信

时间:2021-02-15 23:57:39

通过网卡,让我们能达到通信,对unix是相当的复杂,组要是socket每一层都是用了不同的通信协议,需要好多设置好多的选项。因此在/dev目录下,并没有网卡对应的文件。

 

socket不仅可以用于各种传输协议的IP的连接。也可以用于内核支持的所有其他地址和协议类型。

套接字是使用socket库函数生成的。然后bind函数等。

socket主要用于协议的选择,通信类型,地址族。

bind函数主要目的是给到戒子分配本地地址。该函数的传递一个sockaddr_type 的参数,定义了本地地址。因为不同地址组的地址类型是不同的。type制定了所需的地址类型。

 

这个时候,我们来点代码

 

#ifndef _LINUX_IN_H
#define _LINUX_IN_H

#include <linux/types.h>
#include <linux/socket.h>

/* Standard well-defined IP protocols.  */
enum {
  IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
  IPPROTO_ICMP = 1,		/* Internet Control Message Protocol	*/
  IPPROTO_IGMP = 2,		/* Internet Group Management Protocol	*/
  IPPROTO_IPIP = 4,		/* IPIP tunnels (older KA9Q tunnels use 94) */
  IPPROTO_TCP = 6,		/* Transmission Control Protocol	*/
  IPPROTO_EGP = 8,		/* Exterior Gateway Protocol		*/
  IPPROTO_PUP = 12,		/* PUP protocol				*/
  IPPROTO_UDP = 17,		/* User Datagram Protocol		*/
  IPPROTO_IDP = 22,		/* XNS IDP protocol			*/
  IPPROTO_RSVP = 46,		/* RSVP protocol			*/
  IPPROTO_GRE = 47,		/* Cisco GRE tunnels (rfc 1701,1702)	*/

  IPPROTO_IPV6	 = 41,		/* IPv6-in-IPv4 tunnelling		*/

  IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
  IPPROTO_AH = 51,             /* Authentication Header protocol       */
  IPPROTO_PIM    = 103,		/* Protocol Independent Multicast	*/

  IPPROTO_COMP   = 108,                /* Compression Header protocol */
  IPPROTO_SCTP   = 132,		/* Stream Control Transport Protocol	*/

  IPPROTO_RAW	 = 255,		/* Raw IP packets			*/
  IPPROTO_MAX
};


/* Internet address. */
struct in_addr {
	__u32	s_addr;
};

#define IP_TOS		1
#define IP_TTL		2
#define IP_HDRINCL	3
#define IP_OPTIONS	4
#define IP_ROUTER_ALERT	5
#define IP_RECVOPTS	6
#define IP_RETOPTS	7
#define IP_PKTINFO	8
#define IP_PKTOPTIONS	9
#define IP_MTU_DISCOVER	10
#define IP_RECVERR	11
#define IP_RECVTTL	12
#define	IP_RECVTOS	13
#define IP_MTU		14
#define IP_FREEBIND	15
#define IP_IPSEC_POLICY	16
#define IP_XFRM_POLICY	17

/* BSD compatibility */
#define IP_RECVRETOPTS	IP_RETOPTS

/* IP_MTU_DISCOVER values */
#define IP_PMTUDISC_DONT		0	/* Never send DF frames */
#define IP_PMTUDISC_WANT		1	/* Use per route hints	*/
#define IP_PMTUDISC_DO			2	/* Always DF		*/

#define IP_MULTICAST_IF			32
#define IP_MULTICAST_TTL 		33
#define IP_MULTICAST_LOOP 		34
#define IP_ADD_MEMBERSHIP		35
#define IP_DROP_MEMBERSHIP		36
#define IP_UNBLOCK_SOURCE		37
#define IP_BLOCK_SOURCE			38
#define IP_ADD_SOURCE_MEMBERSHIP	39
#define IP_DROP_SOURCE_MEMBERSHIP	40
#define IP_MSFILTER			41
#define MCAST_JOIN_GROUP		42
#define MCAST_BLOCK_SOURCE		43
#define MCAST_UNBLOCK_SOURCE		44
#define MCAST_LEAVE_GROUP		45
#define MCAST_JOIN_SOURCE_GROUP		46
#define MCAST_LEAVE_SOURCE_GROUP	47
#define MCAST_MSFILTER			48

#define MCAST_EXCLUDE	0
#define MCAST_INCLUDE	1

/* These need to appear somewhere around here */
#define IP_DEFAULT_MULTICAST_TTL        1
#define IP_DEFAULT_MULTICAST_LOOP       1

/* Request struct for multicast socket ops */

struct ip_mreq 
{
	struct in_addr imr_multiaddr;	/* IP multicast address of group */
	struct in_addr imr_interface;	/* local IP address of interface */
};

struct ip_mreqn
{
	struct in_addr	imr_multiaddr;		/* IP multicast address of group */
	struct in_addr	imr_address;		/* local IP address of interface */
	int		imr_ifindex;		/* Interface index */
};

struct ip_mreq_source {
	__u32		imr_multiaddr;
	__u32		imr_interface;
	__u32		imr_sourceaddr;
};

struct ip_msfilter {
	__u32		imsf_multiaddr;
	__u32		imsf_interface;
	__u32		imsf_fmode;
	__u32		imsf_numsrc;
	__u32		imsf_slist[1];
};

#define IP_MSFILTER_SIZE(numsrc) \
	(sizeof(struct ip_msfilter) - sizeof(__u32) \
	+ (numsrc) * sizeof(__u32))

struct group_req
{
	__u32				 gr_interface;	/* interface index */
	struct __kernel_sockaddr_storage gr_group;	/* group address */
};

struct group_source_req
{
	__u32				 gsr_interface;	/* interface index */
	struct __kernel_sockaddr_storage gsr_group;	/* group address */
	struct __kernel_sockaddr_storage gsr_source;	/* source address */
};

struct group_filter
{
	__u32				 gf_interface;	/* interface index */
	struct __kernel_sockaddr_storage gf_group;	/* multicast address */
	__u32				 gf_fmode;	/* filter mode */
	__u32				 gf_numsrc;	/* number of sources */
	struct __kernel_sockaddr_storage gf_slist[1];	/* interface index */
};

#define GROUP_FILTER_SIZE(numsrc) \
	(sizeof(struct group_filter) - sizeof(struct __kernel_sockaddr_storage) \
	+ (numsrc) * sizeof(struct __kernel_sockaddr_storage))

struct in_pktinfo
{
	int		ipi_ifindex;
	struct in_addr	ipi_spec_dst;
	struct in_addr	ipi_addr;
};

/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
struct sockaddr_in {
  sa_family_t		sin_family;	/* Address family		*/
  unsigned short int	sin_port;	/* Port number			*/
  struct in_addr	sin_addr;	/* Internet address		*/

  /* Pad to size of `struct sockaddr'. */
  unsigned char		__pad[__SOCK_SIZE__ - sizeof(short int) -
			sizeof(unsigned short int) - sizeof(struct in_addr)];
};

 

重点看红色部分,
sin_family;地址族 unsigned short int sin_port; 端口号

 struct in_addr sin_addr; 网络地址。

 

在这里考虑到网络字节序还有大端小端的问题。

下面有个简单的例子。

使用了基本的用户函数 socket,bind,listion select 函数

<strong><span style="font-size: 18px;">首先 运行在linux 运行gcc -g -o select select.c</span></strong><pre class="cpp" name="code">/ *******select.c*********/
/ *******Using select() for I/O multiplexing */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* port we're listening on */
#define PORT 2020

int main(int argc, char *argv[])
{
	/*定义描述符集合 */
	fd_set master;
	/* 定义select函数可读的描述符*/
	fd_set read_fds;
	/* 服务器地址 */
	struct sockaddr_in serveraddr;
	/* 客户地址 */
	struct sockaddr_in clientaddr;
	/* 定义最大描述符数 */
	int fdmax;
	/* 监听描述符 */
	int listener;
	/* accept描述符 */
	int newfd;
	/* 缓冲 */
	char buf[1024];
	int nbytes;
	/* 设置 setsockopt() SO_REUSEADDR */
	int yes = 1;
	int addrlen;
	int i, j;
	/* 清除 */
	FD_ZERO(&master);
	FD_ZERO(&read_fds);

	/* 连接 */
	if((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("Server-socket() error lol!");

		exit(1);
	}
	printf("Server-socket() is OK...\n");
	/*判断地址以用 */
	if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	{
		perror("Server-setsockopt() error lol!");
		exit(1);
	}
	printf("Server-setsockopt() is OK...\n");

	/* bind */
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = INADDR_ANY;
	serveraddr.sin_port = htons(PORT);
	memset(&(serveraddr.sin_zero), '\0', 8);

	if(bind(listener, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
	{
		perror("Server-bind() error lol!");
		exit(1);
	}
	printf("Server-bind() is OK...\n");

	/* listen */
	if(listen(listener, 10) == -1)
	{
		perror("Server-listen() error lol!");
		exit(1);
	}
	printf("Server-listen() is OK...\n");

	/* 把 listener 加到 master set */
	FD_SET(listener, &master);

	/* keep track of the biggest file descriptor */
	fdmax = listener; /* so far, it's this one*/

	/* 循环了 */
	for(;;)
	{
		/* copy it */
		read_fds = master;

		if(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
		{
			perror("Server-select() error lol!");
			exit(1);
		}
		printf("Server-select() is OK...\n");

		/*从存在的描述符中查找要读的数据*/
		for(i = 0; i <= fdmax; i++)
		{
			if(FD_ISSET(i, &read_fds))
			{ /* 得到一个... */
				if(i == listener)
				{
					/* h处理这个的连接 */
					addrlen = sizeof(clientaddr);
					if((newfd = accept(listener, (struct sockaddr *)&clientaddr, &addrlen)) == -1)
					{
						perror("Server-accept() error lol!");
					}
					else
					{
						printf("Server-accept() is OK...\n");

						FD_SET(newfd, &master); /* add to master set */
						if(newfd > fdmax)
						{ /* keep track of the maximum */
							fdmax = newfd;
						}
						printf("%s: New connection from %s on socket %d\n", argv[0], inet_ntoa(clientaddr.sin_addr), newfd);
					}
				}
				else
				{
					/* 处理从客户端来的数据*/
					if((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0)
					{
						/* 没有数据 */
						if(nbytes == 0)
							/* 断开连接。 */
							printf("%s: socket %d hung up\n", argv[0], i);

						else
							perror("recv() error lol!");

						/* 关闭... */
						close(i);
						/* 删除这个连接符 */
						FD_CLR(i, &master);
					}
					else
					{
						/* we got some data from a client*/
						for(j = 0; j <= fdmax; j++)
						{
							/* 发给每个人! */
							if(FD_ISSET(j, &master))
							{
								/* 除了监听的和自己的。 */
								if(j != listener && j != i)
								{
									if(send(j, buf, nbytes, 0) == -1)
										perror("send() error lol!");
								}
							}
						}
					}
				}
			}
		}
	}
	return 0;
}</pre><br>
<pre></pre>
<pre class="cpp" name="code"><strong><span style="font-size: 18px;">然后在运行 ./select & 或者ctrl+z 让他后台运行</span></strong></pre><pre class="cpp" name="code"><span style="font-size: 18px;"><strong>开一个窗口 运行telnet localhostip 2020</strong> </span></pre><pre class="cpp" name="code"><span style="font-size: 18px;">程序如下</span></pre><pre class="cpp" name="code"></pre>
<p>更多文章,欢迎访问<a href="http://blog.csdn.net/wallwind">http://blog.csdn.net/wallwind</a> 转载请注明出处</p>