socket 是“套接字”的意思,是计算机之间进行通信的一种约定,也可以认为是一种技术。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
socket里面最底层为:
typedef __uint32_t in_addr_t; /* base type for internet address */
接着它被放到了一个叫做in_addr的结构体里面了,作为后面的使用。
/*
* Internet address (a structure for historical reasons)
*/
struct in_addr {
in_addr_t s_addr;
};
再接着它被放到了sockaddr_in这个结构体里面了,即socket地址,它为internet方式。
/*
* Socket address, internet style.
*/
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
在socket.h文件里面再次定义了socket地址,里面包含它的长度,地址族,和地址值。
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
那么接下来看看地址族(address family)吧。
/*
* Address families.
*/
#define AF_UNSPEC 0 /* unspecified */
#define AF_UNIX 1 /* local to host (pipes) */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define AF_LOCAL AF_UNIX /* backward compatibility */
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define AF_INET 2 /* internetwork: UDP, TCP, etc. */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define AF_IMPLINK 3 /* arpanet imp addresses */
#define AF_PUP 4 /* pup protocols: e.g. BSP */
#define AF_CHAOS 5 /* mit CHAOS protocols */
#define AF_NS 6 /* XEROX NS protocols */
#define AF_ISO 7 /* ISO protocols */
#define AF_OSI AF_ISO
#define AF_ECMA 8 /* European computer manufacturers */
#define AF_DATAKIT 9 /* datakit protocols */
#define AF_CCITT 10 /* CCITT protocols, X.25 etc */
#define AF_SNA 11 /* IBM SNA */
#define AF_DECnet 12 /* DECnet */
#define AF_DLI 13 /* DEC Direct data link interface */
#define AF_LAT 14 /* LAT */
#define AF_HYLINK 15 /* NSC Hyperchannel */
#define AF_APPLETALK 16 /* Apple Talk */
#define AF_ROUTE 17 /* Internal Routing Protocol */
#define AF_LINK 18 /* Link layer interface */
#define pseudo_AF_XTP 19 /* eXpress Transfer Protocol (no AF) */
#define AF_COIP 20 /* connection-oriented IP, aka ST II */
#define AF_CNT 21 /* Computer Network Technology */
#define pseudo_AF_RTIP 22 /* Help Identify RTIP packets */
#define AF_IPX 23 /* Novell Internet Protocol */
#define AF_SIP 24 /* Simple Internet Protocol */
#define pseudo_AF_PIP 25 /* Help Identify PIP packets */
/*define pseudo_AF_BLUE 26 Identify packets for Blue Box - Not used */
#define AF_NDRV 27 /* Network Driver 'raw' access */
#define AF_ISDN 28 /* Integrated Services Digital Network*/
#define AF_E164 AF_ISDN /* CCITT E.164 recommendation */
#define pseudo_AF_KEY 29 /* Internal key-management function */
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define AF_INET6 30 /* IPv6 */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define AF_NATM 31 /* native ATM access */
#define AF_SYSTEM 32 /* Kernel event messages */
#define AF_NETBIOS 33 /* NetBIOS */
#define AF_PPP 34 /* PPP communication protocol */
#define pseudo_AF_HDRCMPLT 35 /* Used by BPF to not rewrite headers
* in interface output routine */
#define AF_RESERVED_36 36 /* Reserved for internal usage */
#define AF_IEEE80211 37 /* IEEE 802.11 protocol */
#define AF_UTUN 38
#define AF_MAX 40
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
这里面包括了常用的AF_INET,其中包括TCP,UDP等等其它的地址族。
而对于协议族,其目前和地址族是一样的。
/*
* Protocol families, same as address families for now.
*/
#define PF_UNSPEC AF_UNSPEC
#define PF_LOCAL AF_LOCAL
#define PF_UNIX PF_LOCAL /* backward compatibility */
#define PF_INET AF_INET
#define PF_IMPLINK AF_IMPLINK
#define PF_PUP AF_PUP
#define PF_CHAOS AF_CHAOS
#define PF_NS AF_NS
#define PF_ISO AF_ISO
#define PF_OSI AF_ISO
#define PF_ECMA AF_ECMA
#define PF_DATAKIT AF_DATAKIT
#define PF_CCITT AF_CCITT
#define PF_SNA AF_SNA
#define PF_DECnet AF_DECnet
#define PF_DLI AF_DLI
#define PF_LAT AF_LAT
#define PF_HYLINK AF_HYLINK
#define PF_APPLETALK AF_APPLETALK
#define PF_ROUTE AF_ROUTE
#define PF_LINK AF_LINK
#define PF_XTP pseudo_AF_XTP /* really just proto family, no AF */
#define PF_COIP AF_COIP
#define PF_CNT AF_CNT
#define PF_SIP AF_SIP
#define PF_IPX AF_IPX /* same format as AF_NS */
#define PF_RTIP pseudo_AF_RTIP /* same format as AF_INET */
#define PF_PIP pseudo_AF_PIP
#define PF_NDRV AF_NDRV
#define PF_ISDN AF_ISDN
#define PF_KEY pseudo_AF_KEY
#define PF_INET6 AF_INET6
#define PF_NATM AF_NATM
#define PF_SYSTEM AF_SYSTEM
#define PF_NETBIOS AF_NETBIOS
#define PF_PPP AF_PPP
#define PF_RESERVED_36 AF_RESERVED_36
#define PF_UTUN AF_UTUN
#define PF_MAX AF_MAX
下面是Socket的类型,最常见的为sock_stream和sock_dgram,分别为TCP和UDP的数据传输方式
/*
* Types
*/
#define SOCK_STREAM 1 /* stream socket */
#define SOCK_DGRAM 2 /* datagram socket */
#define SOCK_RAW 3 /* raw-protocol interface */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define SOCK_RDM 4 /* reliably-delivered message */
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define SOCK_SEQPACKET 5 /* sequenced packet stream */
SOCK_STREAM
表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。SOCK_DGRAM
表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。
对于TCP和UDP的区别如下
TCP是面向连接的,交互双方的进程各自建立一个流式套接字,服务器需要等待客户端向其提出连接申请。一旦接受客户端申请就立刻返回一个新的套接字描述符。通过该描述符调用数据传输函数与客户端进行数据的收发。
UDP是面向无连接的,双方建立的是数据报套接字,服务器和客户端在进行传描数据之前不需要进行连接的申请和建立,可以随时向对方发消息。
协议 | TCP | UDP |
---|---|---|
数据传输方式 | 流式 | 数据报 |
优点 | 可靠、稳定 | 速度快,比TCP稍安全 |
缺点 | 速度慢、效率低、占用系统资源高、易被攻击 | 不可靠、不稳定 |
适合场景 | 网络通讯质量要求高(可靠、稳定) | 网络通讯质量要求不高,速度快。 |
需要区分协议与服务、端口的区别:
服务一般和端口对应,比如Web服务对应80端口,而FTP服务对应21端口,SMTP服务对应25端口
而协议指的是类似于TCP等协议。服务中用到协议。比如FTP中用到FTP协议。
这是服务器端的代码:
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <zconf.h>
int UDP()
{
int server_sockfd;
ssize_t len;
struct sockaddr_in my_addr; //服务器网络地址结构体
struct sockaddr_in remote_addr; //客户端网络地址结构体
socklen_t sin_size;
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&my_addr, 0, sizeof(my_addr)); //数据初始化--清零
my_addr.sin_family = AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr = INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port = htons(8000); //服务器端口号
/*创建服务器端套接字--IPv4协议,面向无连接通信,UDP协议*/
if((server_sockfd = socket(PF_INET, SOCK_DGRAM, 0))<0)
{
perror("socket error");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if (bind(server_sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0)
{
perror("bind error");
return 1;
}
printf("my address %s\n", inet_ntoa(my_addr.sin_addr));
sin_size = sizeof(struct sockaddr_in);
printf("waiting for a packet...\n");
/*接收客户端的数据并将其发送给客户端--recvfrom是无连接的*/
if((len = recvfrom(server_sockfd, buf, BUFSIZ, 0, (struct sockaddr *)&remote_addr, &sin_size)) < 0)
{
perror("recvfrom error");
return 1;
}
printf("received packet from %s:\n", inet_ntoa(remote_addr.sin_addr));
buf[len] = '\0';
printf("contents: %s\n", buf);
/*关闭套接字*/
close(server_sockfd);
return 0;
}
int TCP()
{
int server_sockfd;//服务器端套接字
int client_sockfd;//客户端套接字
ssize_t len;
struct sockaddr_in my_addr; //服务器网络地址结构体
struct sockaddr_in remote_addr; //客户端网络地址结构体
socklen_t sin_size;
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&my_addr, 0, sizeof(my_addr)); //数据初始化--清零
my_addr.sin_family = AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr = INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port = htons(8000); //服务器端口号
/*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/
if((server_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket error");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if(bind(server_sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0)
{
perror("bind error");
return 1;
}
printf("my address %s\n", inet_ntoa(my_addr.sin_addr));
/*监听连接请求--监听队列长度为5*/
if(listen(server_sockfd, 5) < 0)
{
perror("listen error");
return 1;
};
sin_size = sizeof(struct sockaddr_in);
/*等待客户端连接请求到达*/
if((client_sockfd = accept(server_sockfd, (struct sockaddr *)&remote_addr, &sin_size))<0)
{
perror("accept error");
return 1;
}
printf("accept client %s\n", inet_ntoa(remote_addr.sin_addr));
len = send(client_sockfd, "Welcome to my server\n", 21, 0);//发送欢迎信息
/*接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数*/
while((len = recv(client_sockfd, buf, BUFSIZ, 0)) > 0)
{
buf[len] = '\0';
printf("%s\n", buf);
if(send(client_sockfd, buf, len, 0) < 0)
{
perror("write error");
return 1;
}
}
/*关闭套接字*/
close(client_sockfd);
close(server_sockfd);
return 0;
}
int main(int argc, char *argv[])
{
UDP();
return 0;
}
这是客户端的代码:
#include <cstdio>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <zconf.h>
int UDP()
{
int client_sockfd;
ssize_t len;
struct sockaddr_in remote_addr; //服务器端网络地址结构体
socklen_t sin_size;
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&remote_addr, 0, sizeof(remote_addr)); //数据初始化--清零
remote_addr.sin_family = AF_INET; //设置为IP通信
remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器IP地址
remote_addr.sin_port = htons(8000); //服务器端口号
/*创建客户端套接字--IPv4协议,面向无连接通信,UDP协议*/
if((client_sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket error");
return 1;
}
strcpy(buf,"This is a test message!"); // 发送的内容
printf("sending: '%s'\n",buf);
sin_size = sizeof(struct sockaddr_in);
/*向服务器发送数据包*/
if((len = sendto(client_sockfd, buf, strlen(buf), 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr))) < 0)
{
perror("recvfrom");
return 1;
}
/*关闭套接字*/
close(client_sockfd);
return 0;
}
int TCP()
{
int client_sockfd;
ssize_t len;
struct sockaddr_in remote_addr; //服务器端网络地址结构体
char buf[BUFSIZ]; //数据传送的缓冲区
memset(&remote_addr, 0, sizeof(remote_addr)); //数据初始化--清零
remote_addr.sin_family = AF_INET; //设置为IP通信
remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器IP地址
remote_addr.sin_port = htons(8000); //服务器端口号
/*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
if((client_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket error");
return 1;
}
/*将套接字绑定到服务器的网络地址上*/
if(connect(client_sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) < 0)
{
perror("Connect error");
return 1;
}
printf("connected to server\n");
len = recv(client_sockfd, buf, BUFSIZ, 0);//接收服务器端信息
buf[len] = '\0';
printf("%s", buf); //打印服务器端信息
/*循环的发送接收信息并打印接收信息(可以按需发送)--recv返回接收到的字节数,send返回发送的字节数*/
while(1)
{
printf("Enter string to send:");
scanf("%s", buf);
if(!strcmp(buf, "quit"))
break;
len = send(client_sockfd, buf, strlen(buf), 0);
len = recv(client_sockfd, buf, BUFSIZ, 0);
buf[len] = '\0';
printf("received:%s\n", buf);
}
/*关闭套接字*/
close(client_sockfd);
return 0;
}
int main(int argc, char *argv[]){
UDP();
return 0;
}