进修LInux,收集编程套接字是基础,也是新手进修的难点,经由历程本篇文章,读者能够经由历程图解、作者的代码完成思绪周全明白IP地点、端口号、TCP、UDP观点、socket API用法、模仿客户端/服务器通讯等。
文章重点:
IP地点、端口号……
socket API
完成UDP客户端/服务器
- 套接字是收集编程中的一种通讯机制,是支撑TCP/IP的收集通讯的基础操纵单位,能够看作是差别主机之间的历程举行双向通讯的端点,简朴的说就是通讯的两方的一种商定,用套接字中的相干函数来完成通讯历程。
前面引见过,当地的历程间通讯(IPC)有很多种体式格局,罕见的总结以下几点:
1、管道(包含无名管道和定名管道); 2、音讯行列; 3、信号量; 4、同享存储。 5、……( Socket和Streams支撑差别主机上的两个历程IPC)。
熟悉收集层通讯历程:
初识IP:
(IP就是:Internet协定IP)
在通讯时,IP有源IP和目标IP之分,
对照寄快递:收集通讯相当于收发快递,IP就是收件/发件人地点,仅仅晓得地点还不可,还要晓得派送人是谁?这就对照于收集中的端口号观点,端口号标识了一个历程,通知操纵系统,当前这个数据交给哪个递次举行剖析。
端口号:
端口号(port)是传输层协定的内容。
端口号是一个2字节16位的整数;
端口号用来标识一个历程,通知操纵系统,当前这个数据交给哪个递次举行剖析;
IP地点 + 端口号能标识收集上的某一台主机的某一个历程;
一个端口号只能被一个历程占用。
端口号 & 历程:
- 观点
历程有唯一的pid标识,端口号也能标识历程;
一个历程能够绑定多个端口号,一个端口号不能被多个历程绑定。
- 源端口号 & 目标端口号
传输层协定(TCP/IP)的数据段中有两个端口号,离别叫做源端口号和目标端口号,就是在形貌“数据是谁的?发给谁?”
TCP:
(TCP)传输掌握协定,面向衔接。是一种供应牢靠数据传输的通用协定。
传输层协定
有衔接
牢靠传输
面向字撙节
UDP:
(UDP)用户数据报协定,是一个面向无衔接的协定。采纳该协定不需要两个应用递次先竖立衔接。UDP协定不供应过失恢复,不能供应数据重传,因而该协定传输数据安全性差。
传输层协定
无衔接
不牢靠传输
面向数据报
收集字节序:
- 怎样定义收集数据流的地点?
实在很轻易明白这个题目,就是C言语中比较考究的大小端题目。
发送机按内存地点从低到高递次发送;
吸收主机按内存地点从低到高递次保留;
TCP/IP划定:收集数据流应采纳大端字节序,即地地点高字节;
不管主机是大端机照样小端机,都必须遵照TCP/IP划定;
假如发送机是小端,就先将数据转成大端再发送。
socket API:
//建立socket文件形貌符 (TCP/UDP,客户端+服务器) int socket(int domain, int type, int protocol);
参数1(domain): 挑选建立的套接字所用的协定族;
AF_INET : IPv4协定;
AF_INET6: IPv6协定;
AF_LOCAL: Unix域协定;
AF_ROUTE:路由套接口;
AF_KEY :密钥套接口。
参数2(type):指定套接口范例,所选范例有:
SOCK_STREAM:字撙节套接字;
SOCK_DGRAM : 数据报套接字;
SOCK_RAW : 原始套接口。
procotol: 运用的特定协定,平常运用默许协定(NULL)。
//绑定端口号 (TCP/IP,服务器) int bind(int socket, const struct sockaddr *address, socklen_t address_len);
参数1(socket) : 是由socket()挪用返回的而且未作衔接的套接字形貌符(套接字号)。
参数2(address):指向特定协定的地点指针。
参数3(address_len):上面地点构造的长度。
返回值:没有毛病,bind()返回0,不然SOCKET_ERROR。
//最先监听socket (TCP,服务器) int listen(int socket, int backlog);
参数1(sockfd):是由socket()挪用返回的而且未作衔接的套接字形貌符(套接字号)。
参数2(backlog):所监听的端口行列大小。
//接收要求 (TCP,服务器) int accept(int socket, struct sockaddr* address, socklen_t* address_len);
参数1(socket) : 是由socket()挪用返回的而且未作衔接的套接字形貌符(套接字号)。
参数2(address):指向特定协定的地点指针。
参数3(addrlen):上面地点构造的长度。
返回值:没有毛病,bind()返回0,不然SOCKET_ERROR。
//竖立衔接 (TCP,客户端) int connect(int sockfd, const struct struct sockaddr *addr, aocklen_t addrlen);
//封闭套接字 int close(int fd);
参数(fd):是由socket()挪用返回的而且未作衔接的套接字形貌符(套接字号)。
socket API是一层笼统的收集编程接口,适用于种种底层收集协定,如IPv4,IPv6,……
简朴的TCP收集递次:
- TCP客户—服务器递次的实行流程图:
服务器代码:
#include<iostream> #include<unistd.h> #include<stdio.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> using namespace std; #define SERVER_PORT 5050 //端口号 #define SERVER_IP "192.168.3.254" //服务器ip #define QUEUE_SIZE 5 //所监听端口行列大小 int main(int argc, char *argv[]) { //建立一个套接字,并检测是不是建立胜利 int sockSer; sockSer = socket(AF_INET, SOCK_STREAM, 0); if(sockSer == -1){ perror("socket"); } //设置端口能够重用,能够多个客户端衔接同一个端口,并检测是不是设置胜利 int yes = 1; if(setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){ perror("setsockopt"); } struct sockaddr_in addrSer,addrCli; //建立一个纪录地点信息的构造体 addrSer.sin_family = AF_INET; //所运用AF_INET协定族 addrSer.sin_port = htons(SERVER_PORT); //设置地点构造体中的端口号 addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //设置个中的服务器ip //将套接字地点与所建立的套接字号联系起来。并检测是不是绑定胜利 socklen_t addrlen = sizeof(struct sockaddr); int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen); if(res == -1) perror("bind"); listen(sockSer, QUEUE_SIZE); //监听端口行列是不是由衔接要求,假如有就将该端口设置位可衔接状况,守候服务器吸收衔接 printf("Server Wait Client Accept......\n"); //假如监听到有衔接要求接收衔接要求。并检测是不是衔接胜利,胜利返回0,不然返回-1 int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen); if(sockConn == -1) perror("accept"); else { printf("Server Accept Client OK.\n"); printf("Client IP:> %s\n", inet_ntoa(addrCli.sin_addr)); printf("Client Port:> %d\n",ntohs(addrCli.sin_port)); } char sendbuf[256]; //请求一个发送缓存区 char recvbuf[256]; //请求一个吸收缓存区 while(1) { printf("Ser:>"); scanf("%s",sendbuf); if(strncmp(sendbuf,"quit",4) == 0) //假如所要发送的数据为"quit",则直接退出。 break; send(sockConn, sendbuf, strlen(sendbuf)+1, 0); //发送数据 recv(sockConn, recvbuf, 256, 0); //吸收客户端发送的数据 printf("Cli:> %s\n",recvbuf); } close(sockSer); //封闭套接字 return 0; }
客户端代码:
#include<iostream> #include<unistd.h> #include<stdio.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> using namespace std; #define SERVER_PORT 5050 #define SERVER_IP "192.168.3.254" int main(int argc, char *argv[]) { //建立客户端套接字号,并检测是不是建立胜利 int sockCli; sockCli = socket(AF_INET, SOCK_STREAM, 0); if(sockCli == -1) perror("socket"); //建立一个地点信息构造体,并对其内容举行设置 struct sockaddr_in addrSer; addrSer.sin_family = AF_INET; //运用AF_INET协定族 addrSer.sin_port = htons(SERVER_PORT); //设置端口号 addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //设置服务器ip bind(sockCli,(struct sockaddr*)&addrCli, sizeof(struct sockaddr)); //将套接字地点与所建立的套接字号联系起来 //建立一个与服务器的衔接,并检测衔接是不是胜利 socklen_t addrlen = sizeof(struct sockaddr); int res = connect(sockCli,(struct sockaddr*)&addrSer, addrlen); if(res == -1) perror("connect"); else printf("Client Connect Server OK.\n"); char sendbuf[256]; //请求一个发送数据缓存区 char recvbuf[256]; //请求一个吸收数据缓存区 while(1) { recv(sockCli, recvbuf, 256, 0); //吸收来自服务器的数据 printf("Ser:> %s\n",recvbuf); printf("Cli:>"); scanf("%s",sendbuf); if(strncmp(sendbuf,"quit", 4) == 0) //假如客户端发送的数据为"quit",则退出。 break; send(sockCli, sendbuf, strlen(sendbuf)+1, 0); //发送数据 } close(sockCli); //封闭套接字 return 0; }
简朴的UDP收集递次:
- 相对与TCP来讲,UDP安全性差,面向无链接。所以UDP地完成少了衔接与吸收衔接的操纵。所以在收发数据时就不能再用send()和recvfrom()了,而是用sendto()和recvto()之名从哪收发数据。
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
参数1(sockfd):是由socket()挪用返回的而且未作衔接的套接字形貌符(套接字号)
参数2(buf):指向存有发送数据的缓冲区的指针
参数3(len):缓冲区长度。
**参数4(flags):**flags的值或为0,或为其他
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
参数1(sockfd):是由socket()挪用返回的而且未作衔接的套接字形貌符(套接字号)
参数2(buf):指向存有吸收数据的缓冲区的指针
参数3(len):缓冲区长度
**参数4(flags):**flags的值或为0,或为其他
服务器端代码:
#include<stdio.h> #include<unistd.h> #include<string.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/socket.h> int main() { //建立一个套接字,并检测是不是建立胜利 int sockSer = socket(AF_INET, SOCK_DGRAM, 0); if(sockSer == -1) perror("socket"); struct sockaddr_in addrSer; //建立一个纪录地点信息的构造体 addrSer.sin_family = AF_INET; //运用AF_INET协定族 addrSer.sin_port = htons(5050); //设置地点构造体中的端口号 addrSer.sin_addr.s_addr = inet_addr("192.168.3.169"); //设置通讯ip //将套接字地点与所建立的套接字号联系起来,并检测是不是绑定胜利 socklen_t addrlen = sizeof(struct sockaddr); int res = bind(sockSer,(struct sockaddr*)&addrSer, addrlen); if(res == -1) perror("bind"); char sendbuf[256]; //请求一个发送数据缓存区 char recvbuf[256]; //请求一个吸收数据缓存区 struct sockaddr_in addrCli; while(1) { recvfrom(sockSer,recvbuf,256,0,(struct sockaddr*)&addrCli, &addrlen); //从指定地点吸收客户端数据 printf("Cli:>%s\n",recvbuf); printf("Ser:>"); scanf("%s",sendbuf); sendto(sockSer,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&addrCli, addrlen); //向客户端发送数据 } return 0; }
客户端代码:
#include<stdio.h> #include<unistd.h> #include<string.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/socket.h> int main() { //建立一个套接字,并检测是不是建立胜利 int sockCli = socket(AF_INET, SOCK_DGRAM, 0); if(sockCli == -1){ perror("socket"); } addrSer.sin_family = AF_INET; //运用AF_INET协定族 addrSer.sin_port = htons(5050); //设置地点构造体中的端口号 addrSer.sin_addr.s_addr = inet_addr("192.168.3.169"); //设置通讯ip socklen_t addrlen = sizeof(struct sockaddr); char sendbuf[256]; //请求一个发送数据缓存区 char recvbuf[256]; //请求一个吸收数据缓存区 while(1){ //向客户端发送数据 printf("Cli:>"); scanf("%s",sendbuf); sendto(sockCli, sendbuf, strlen(sendbuf)+1, 0, (struct sockaddr*)&addrSer, addrlen); 吸收来自客户端的数据 recvfrom(sockCli, recvbuf, BUFFER_SIZE, 0, (struct sockaddr*)&addrSer, &addrlen); printf("Ser:>%s\n", recvbuf); } return 0; }
【引荐课程:TCP/IP视频教程】
以上就是socket套接字详解(TCP与UDP)的细致内容,更多请关注ki4网别的相干文章!