Linux System Programming 67 Network Programming 1-Report Socket Transmission

Need to master:

socket(),获取SOCKET
bind(), 绑定SOCKET到本地地址
inet_pton(), IP地址格式转换:点分式 --> 大整数
recvfrom(),用于报式套接字,从 SOCKET上接收信息
recv(), 用于流式套接字,从 SOCKET上接收信息
inet_ntop(),IP地址格式转换:大整数格式 --> 点分式格式
send(), 用于流式套接字 向SOCKET发送数据
sendto(),用于报式套接字,向SOCKET发送数据

Note for cross-host communication:

Passive end (run first, wait to be accessed)

1 取得SOCKET  :socket()
2 给SOCKET取得地址,即绑定本地地址(包含端口,ip等信息): bind()
3 收/发消息  :recvfrom()
4 关闭SOCKET: close()

Active end

1 取得SOCKET
2 给SOCKET取得地址(可省略)
3 发/收消息:sendto()
4 关闭SOCKET

Why can the bind() on the active end, that is, bind the local address operation be omitted?

bind() is to obtain the address for SOCKET, that is, bind the local address. This operation is an agreement with this machine.
If the sender does not agree on the address with this machine, the bind() operation is omitted. After the current SOCKET is successfully established. The system will assign us an available free address for us to use, and the port will be used for us until the end of the process.


socket()

NAME
创建通信端点,取得SOCKET
       socket - create an endpoint for communication

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

/* Use a protocol in the protocol family domain to complete the type type transmission

Note: If there are one or more protocols in the target protocol family that can support the target transmission type, then the protocol protocol can be written as 0, which means that the protocol in the protocol family that supports the target transmission type by default

For example: soccket(AF_INET,SOCK_DGRAM, 0); means that the protocol family AF_INET protocol family supports the protocol socket by default to complete the socket SOCK_DGRAM socket transmission,

domain: domain, such as protocol family
type: how the upper layer implements
protocol: protocol
*/

   int socket(int domain, int type, int protocol);

RETURN VALUE
On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errno is set appropriately.

domain :
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   Local communication 本地协议              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7)
       AF_IPX              IPX - Novell protocols
       AF_NETLINK          Kernel user interface device     netlink(7)
       AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
       AF_AX25             Amateur radio AX.25 protocol 无线电 短波通信
       AF_ATMPVC           Access to raw ATM PVCs
       AF_APPLETALK        AppleTalk                        ddp(7)
       AF_PACKET           Low level packet interface       packet(7)
       AF_ALG              Interface to kernel crypto API

type:

SOCK_STREAM     流式套接字,Provides sequenced, reliable, two-way, connection-based byte streams.
有序可靠:只要接收方能够接到数据,就可以保证 当前数据包中的内容是正确的。不能保证不丢包,网络传输中会有丢包
双工
基于连接:点对点,一对一
字节流传输

       SOCK_DGRAM      报式
数据分组
无连接的
不可靠的

SOCK_SEQPACKET 有序可靠的报式
有序可靠的报式

...

bind()

NAME
给SOCKET绑定一个地址
       bind - bind a name to a socket

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

/*
sockfd: Get the file descriptor returned by SOCKET

addr: protocol address, which depends on the address information in the currently used protocol family. The protocol address type in the AF_INET protocol family is struct sockaddr_in

addrlen: protocol address space size

*/

   int bind(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);



sockaddr 依赖于当前用到的协议族中的地址信息 The sockaddr structure is defined as something like:

       struct sockaddr {
           sa_family_t sa_family;
           char        sa_data[14];
       }

Different protocol families use different structures to bind their own addresses. So there is no struct sockaddr type. So our processing method is: which protocol family we are using, we use the protocol family address as addr, and then write the address length to addrlen

AF_INET see ip (7)

man 7 ip

/*
Note: The IP address and port must be sent along with the network. Represent one's own identity
*/

           struct sockaddr_in {
				//协议族 address family: AF_INET
               sa_family_t    sin_family; 

 	   		    //需要的端口
               in_port_t      sin_port;  

//IP地址 并非点分式,而是大整数internet address ,用的时候需要格式转换:inet_pton()
               struct in_addr sin_addr;   
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };

So the protocol address type in the AF_INET protocol family is struct sockaddr_in

RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.


inet_pton() IP address format conversion: dot fraction --> big integer

NAME
将  IPv4 and IPv6 addresses 转换为 二进制格式,即将点分式 转换为 二进制格式
       inet_pton - convert IPv4 and IPv6 addresses from text to binary form

SYNOPSIS
       #include <arpa/inet.h>

/*
af: Protocol family, only AF_INET or AF_INET6, namely IPV4 or IPV6
src: IP address
dst: storage space after conversion
*/

   int inet_pton(int af, const char *src, void *dst);

0.0.0.0: It can match any IP address, which means what is our own IP address at this stage of binding, and 0.0.0.0 will be replaced with our current IP address


recvfrom() Receive information from SOCKET

NAME
从 SOCKET上接收信息
       recv, recvfrom, recvmsg - receive a message from a socket

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

/* For streaming socket
sockfd: target SOCKET
buf: receiving information storage address
len: receiving information storage space size
flags: special requirements
*/

   ssize_t recv(int sockfd, void *buf, size_t len, int flags);

/* recvfrom is used for reporting socket
sockfd: target SOCKET
buf: receiving information storage address
len: receiving information storage space size
flags: special requirements
src_addr: sender address, that is, the address of the opposite end
addrlen: sender address space size, that is The opposite address represents the size of the space
*/

   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);

recv() is applied to streaming sockets, only need to specify the target socket, the location of the stored information, namely the size, and whether there are special requirements. Because the link is established in advance, one-to-one, point-to-point connection, so there is no need to record who the opposite end is.

recvfrom() is used for report socket communication. The source of each message received may be inconsistent, so in addition to the need to specify the target socket, the location and size of the stored information, and whether there are special requirements. It is also necessary to record who the other party is, that is, the identity of the sender.


inet_ntop() big integer format --> dotted format

NAME 
转换IPv4 and IPv6 addresses 地址格式, 大整数格式 --> 点分式格式
       inet_ntop - convert IPv4 and IPv6 addresses from binary to text form

SYNOPSIS
       #include <arpa/inet.h>

/*
af: protocol family
src: the IP address to be converted
dst: change the target IP to this address
size: the size of the space pointed to by dst

*/

   const char *inet_ntop(int af, const void *src,
                         char *dst, socklen_t size);

send()、sendto()

NAME
       send, sendto, sendmsg - send a message on a socket

SYNOPSIS
       #include <sys/types.h>
       #include <sys/socket.h>

/* Used for streaming socket to send data
sockfd: target SOCKET
buf: send data information storage address
len: send data information storage space size
*/

   ssize_t send(int sockfd, const void *buf, size_t len, int flags);

/* Used to send data with report socket
sockfd: target SOCKET
buf: send data information storage address
len: send data information storage space size
flags: special requirements
dest_addr: receiver address, that is, the address of the opposite end
addrlen: receiver address space Size
*/

   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);

RETURN VALUE
On success, these calls return the number of bytes sent. On error, -1 is returned, and errno is set appropriately.

Experiment: report-style socket transfer data

proto.h

#ifndef PROTO_H_
#define PROTO_H_

#define RCVPORT "1989"
#define NAMESIZE 11
#define IPSTRSIZE 40
struct msg_st
{
	char name[NAMESIZE];
	uint32_t math;
	uint32_t chinese;
}__attribute__((pack));

#endif

rcver.c receiver

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include "proto.h"

int main()
{
	int sd;
	struct sockaddr_in laddr,raddr;
	struct msg_st rbuf;
	socklen_t raddr_len;
	char ipstr[IPSTRSIZE];

//取得SOCKET, 用 AF_INET协议族中 默认支持报式套接字的协议 来完成 报式SOCK_DGRAM套接字传输,
	sd = socket(AF_INET,SOCK_DGRAM,0);//IPPROTO_UDP
	if(sd < 0)
	{
		perror("soccket()");
		exit(1);
	}

//设置  AF_INET 协议族地址信息结构体,AF_INET 协议族中的 协议地址类型为 struct sockaddr_in 
 // 协议族为 AF_INET 
 laddr.sin_family = AF_INET;
// 设置端口为1989,因为需要将自己的地址信息(包括端口信息)发出去,所以需要注意字节序问题,即 从主机发向网络,htons
	laddr.sin_port = htons(atoi(RCVPORT)); 

	inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);//0.0.0.0:any address

// 给SOCKET绑定一个地址,关联到目标协议族地址信息结构体
	if(bind(sd,(void *)&laddr,sizeof(laddr)) < 0)
	{
		perror("bind");
		exit(1);
	}

	/* !!!! *///一定要注意初始化对端地址空间大小信息
	raddr_len = sizeof(raddr);

	while(1)
	{
//以报式套接字方式 从 SOCKET上接收信息,所以需要指定对端地址信息,remote端地址信息。接收到的信息存储到rbuf
		recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len);
		
// 转换IPv4 and IPv6 addresses 地址格式, 大整数格式 --> 点分式格式 存储到ipstr
		inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);

// raddr.sin_port) 是从socket接收到的信息,不是单字节信息,需要字节序转换 ntohs
		printf("---MESSAGE FROM %s:%d---\n",ipstr,ntohs(raddr.sin_port));

		printf("---NAME = %s\n",rbuf.name);//单字节信息,不需要字节序转换
		printf("---MATH = %d\n",ntohl(rbuf.math));//需要字节序转换
		printf("---CHINESE = %d\n",ntohl(rbuf.chinese));//需要字节序转换
	}	

	close(sd);

	exit(0);

}

Sending end:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "proto.h"

int main(int argc,char *argv[])
{
	int sd;
	struct sockaddr_in raddr;
	struct msg_st sbuf;

	if(argc < 2)
	{
		fprintf(stderr,"Usage ...\n");
		exit(1);
	}


	sd = socket(AF_INET,SOCK_DGRAM,0);//IPPROTO_UDP
	if(sd < 0)
	{
		perror("soccket()");
		exit(1);
	}

	//bind()

	strcpy(sbuf.name,"MHR");
	sbuf.math = htonl(rand()%100);
	sbuf.chinese = htonl(rand()%100);
	
	raddr.sin_family = AF_INET;
	raddr.sin_port = htons(atoi(RCVPORT));
	inet_pton(AF_INET,argv[1],&raddr.sin_addr);


	if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr)) < 0)
	{
		perror("sendto()");
		exit(1);
	}

	puts("OK");

	close(sd);

	exit(0);

}

It can be seen from the experimental results that the data transmission was successful. And because there is no bind() on the active end, the ports assigned to the active end by the transmission system in the experiment are 45499, 56709, and 51977 respectively. The passive end prints information such as the IP port data information of the active end.

Insert picture description here
View network status

netstat -anu  //u:udp 报式套接字
netstat -ant   // t:tcp 流式套接字

mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ netstat -anu
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
udp        0      0 0.0.0.0:1989            0.0.0.0:*       //可以看到 1989端口 已经打开     
udp        0      0 127.0.1.1:53            0.0.0.0:*                          
udp        0      0 0.0.0.0:68              0.0.0.0:*                          
udp        0      0 0.0.0.0:631             0.0.0.0:*                          
udp        0      0 0.0.0.0:51842           0.0.0.0:*                          
udp        0      0 0.0.0.0:5353            0.0.0.0:*                          
udp6       0      0 :::59961                :::*                               
udp6       0      0 :::5353                 :::*                               
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ 


 netstat -nap |grep address number 查看程序运行的pid
如:
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ netstat -nap |grep 0.0.0.0 //代码中被动端地址 是0.0.0.0
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      -               
udp        0      0 0.0.0.0:1989            0.0.0.0:*                           2739/a.out    //进程IP 2739   
udp        0      0 127.0.1.1:53            0.0.0.0:*                           -               
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -               
udp        0      0 0.0.0.0:631             0.0.0.0:*                           -               
udp        0      0 0.0.0.0:51842           0.0.0.0:*                           -               
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           -               
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ kill 2739
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ netstat -nap |grep 0.0.0.0
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.1.1:53            0.0.0.0:*               LISTEN      -               
udp        0      0 127.0.1.1:53            0.0.0.0:*                           -               
udp        0      0 0.0.0.0:68              0.0.0.0:*                           -               
udp        0      0 0.0.0.0:631             0.0.0.0:*                           -               
udp        0      0 0.0.0.0:51842           0.0.0.0:*                           -               
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           -               
mhr@ubuntu:~/Desktop/xitongbiancheng/socket/1$ 

Guess you like

Origin blog.csdn.net/LinuxArmbiggod/article/details/114990370