旗下导航:搜·么
当前位置:网站首页 > PHP教程 > 正文

socket套接字详解(TCP与UDP)【php教程】

作者:搜搜PHP网发布时间:2019-11-26分类:PHP教程浏览:106


导读:进修LInux,收集编程套接字是基础,也是新手进修的难点,经由历程本篇文章,读者能够经由历程图解、作者的代码完成思绪周全明白IP地点、端口号、TCP、UDP观点、socketA...

进修LInux,收集编程套接字是基础,也是新手进修的难点,经由历程本篇文章,读者能够经由历程图解、作者的代码完成思绪周全明白IP地点、端口号、TCP、UDP观点、socket API用法、模仿客户端/服务器通讯等。

  • Mark:浏览blog + 代码完成耗时18分钟
文章重点:
  • 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网别的相干文章!

标签:TCPUDPsocket