習得する必要があります:
socket(),获取SOCKET
bind(), 绑定SOCKET到本地地址
inet_pton(), IP地址格式转换:点分式 --> 大整数
recvfrom(),用于报式套接字,从 SOCKET上接收信息
recv(), 用于流式套接字,从 SOCKET上接收信息
inet_ntop(),IP地址格式转换:大整数格式 --> 点分式格式
send(), 用于流式套接字 向SOCKET发送数据
sendto(),用于报式套接字,向SOCKET发送数据
クロスホスト通信に関する注意:
パッシブエンド(最初に実行し、アクセスされるのを待ちます)
1 取得SOCKET :socket()
2 给SOCKET取得地址,即绑定本地地址(包含端口,ip等信息): bind()
3 收/发消息 :recvfrom()
4 关闭SOCKET: close()
アクティブエンド
1 取得SOCKET
2 给SOCKET取得地址(可省略)
3 发/收消息:sendto()
4 关闭SOCKET
アクティブエンドのbind()、つまりローカルアドレスのバインド操作を省略できるのはなぜですか?
bind()は、SOCKETのアドレスを取得すること、つまりローカルアドレスをバインドすることです。この操作は、このマシンとの合意です。
送信者が本機のアドレスに同意しない場合、bind()操作は省略されます。現在のSOCKETが正常に確立された後。システムは、使用可能な無料のアドレスを割り当て、プロセスが終了するまでポートが使用されます。
ソケット()
NAME
创建通信端点,取得SOCKET
socket - create an endpoint for communication
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/ *プロトコルファミリドメインのプロトコルを使用して、タイプタイプの送信を完了します
注:ターゲットプロトコルファミリにターゲット伝送タイプをサポートできるプロトコルが1つ以上ある場合、プロトコルプロトコルは0と記述できます。これは、デフォルトでターゲット伝送タイプをサポートするプロトコルファミリのプロトコルを意味します。
次に例を示します。soccket(AF_INET、SOCK_DGRAM、0);は、プロトコルファミリAF_INETプロトコルファミリがデフォルトでプロトコルソケットをサポートして、ソケットSOCK_DGRAMソケット送信を完了することを意味します。
ドメイン:プロトコルファミリ
タイプなどのドメイン:上位層が
プロトコルを実装する方法:プロトコル
* /
int socket(int domain, int type, int protocol);
RETURN VALUEは、ファイル記述子を返します。
成功すると、新しいソケットのファイル記述子が返されます。エラーが発生すると、-1が返され、errnoが適切に設定されます。
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 有序可靠的报式
有序可靠的报式
...
練る()
NAME
给SOCKET绑定一个地址
bind - bind a name to a socket
SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/ *
sockfd:SOCKETによって返されるファイル記述子を取得します
addr:プロトコルアドレス。現在使用されているプロトコルファミリのアドレス情報によって異なります。AF_INETプロトコルファミリのプロトコルアドレスタイプはstructsockaddr_inです。
addrlen:プロトコルアドレス空間サイズ
* /
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];
}
異なるプロトコルファミリは、異なる構造を使用して独自のアドレスをバインドします。したがって、structsockaddrタイプはありません。したがって、処理方法は次のとおりです。使用しているプロトコルファミリ、プロトコルファミリアドレスをaddrとして使用し、アドレス長をaddrlenに書き込みます。
AF_INET ip(7)を参照
男7ip
/ *
注:IPアドレスとポートはネットワークと一緒に送信する必要があります。自分のアイデンティティを表す
* /
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 */
};
したがって、AF_INETプロトコルファミリのプロトコルアドレスタイプはstructsockaddr_inです。
戻り値
成功すると、ゼロが返されます。エラーの場合、-1が返され、errnoが適切に設定されます。
inet_pton()IPアドレス形式の変換:ドットの割合->大きな整数
NAME
将 IPv4 and IPv6 addresses 转换为 二进制格式,即将点分式 转换为 二进制格式
inet_pton - convert IPv4 and IPv6 addresses from text to binary form
SYNOPSIS
#include <arpa/inet.h>
/ *
af:プロトコルファミリ、AF_INETまたはAF_INET6のみ、つまりIPV4またはIPV6
src:IPアドレス
dst:変換後のストレージスペース
* /
int inet_pton(int af, const char *src, void *dst);
0.0.0.0:任意のIPアドレスと一致できます。つまり、バインドのこの段階での独自のIPアドレスは何であり、0.0.0.0は現在のIPアドレスに置き換えられます。
recvfrom()SOCKETから情報を受信します
NAME
从 SOCKET上接收信息
recv, recvfrom, recvmsg - receive a message from a socket
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
/ *ストリーミングソケットに使用
sockfd:ターゲットSOCKET
buf:情報ストレージアドレスの
受信len:情報ストレージスペースのサイズ
フラグの受信:特別な要件
* /
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
/ * recvfromはソケットのレポートに使用されます
sockfd:ターゲットSOCKET
buf:情報ストレージアドレスの
受信len:情報ストレージスペースのサイズの受信
フラグ:特別な要件
src_addr:送信者アドレス、つまり反対
側のアドレスaddrlen:送信者アドレススペースのサイズ、つまり、反対のアドレスはスペースのサイズを表します
* /
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
recv()はストリーミングソケットに適用され、ターゲットソケット、保存された情報の場所、つまりサイズ、および特別な要件があるかどうかを指定するだけで済みます。リンクは事前に確立されているため、1対1のポイントツーポイント接続であり、反対側が誰であるかを記録する必要はありません。
recvfrom()は、レポートソケット通信に使用されます。受信した各メッセージのソースに一貫性がない可能性があるため、ターゲットソケット、保存された情報の場所とサイズ、および特別な要件があるかどうかを指定する必要があります。また、相手が誰であるか、つまり送信者のIDを記録する必要があります。
inet_ntop()ビッグ整数形式->ドット形式
NAME
转换IPv4 and IPv6 addresses 地址格式, 大整数格式 --> 点分式格式
inet_ntop - convert IPv4 and IPv6 addresses from binary to text form
SYNOPSIS
#include <arpa/inet.h>
/ *
af:プロトコルファミリ
src:変換するIPアドレス
dst:ターゲットIPをこのアドレス
サイズに変更: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>
/ *データを送信するためのストリーミングソケットに使用
sockfd:ターゲットSOCKET
buf:データ情報ストレージアドレスを送信
len:データ情報ストレージスペースサイズを送信
* /
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
/ *レポートソケットにデータを送信するために使用
sockfd:ターゲットSOCKET
buf:データ情報ストレージアドレスを送信
len:データ情報ストレージスペースサイズ
フラグを送信:特別な要件
dest_addr:レシーバーアドレス、つまり反対側のアドレス
addrlen:レシーバーアドレス空間サイズ
* /
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
戻り値
成功すると、これらの呼び出しは送信されたバイト数を返します。エラーの場合、-1が返され、errnoが適切に設定されます。
実験:レポートスタイルのソケット転送データ
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レシーバー
#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);
}
送信終了:
#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);
}
実験結果から、データ送信が成功したことがわかります。また、アクティブエンドにはbind()がないため、実験で伝送システムによってアクティブエンドに割り当てられたポートは、それぞれ45499、56709、および51977です。パッシブエンドは、アクティブエンドのIPポートデータ情報などの情報を出力します。
ネットワークステータスを表示する
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$