Socket网络编程基础理解(TCP/UDP)

image

1、什么是套接字:

套接字是网络数据传输用的软件设备;

2、TCP

tcp是基于连接的通信方式。

2.1 服务端

2.1.1 调用socket函数生成“套接字”(相当于安装电话)

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

domain:套接字中的协议族(protocol family)。
type:套接字数据传输类型信息。
protocol:计算机间通信中使用的协议信息。
返回值:成功时返回文件描述符,失败时返回-1;

2.1.2调用bind函数绑定ip和端口号(相当于给电话分配电话号码)

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

sockfd:将要分配地址信息的套接字文件描述符。
myaddr:存有地址信息的结构体变量地址值。
addrlen:第二个变量的长度。
返回值:成功时返回0,失败时返回-1;

2.1.3调用listen函数把套接字转化成可接收的状态(相当于连接电话线)

int listen(int sockfd, int backlog);

sockfd:希望进入等待连接请求状态的套接字描述符,传递的描述符套接字成为服务器端套接字。
backlog:队列长度。
返回值:成功时返回0,失败时返回-1;

2.1.4接受对方的连接请求(相当于可以拿起电话接电话了)

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

sockfd:服务器文件描述符。
addr:客户端地址信息的变量地址。
addrlen:存有第二个变量地址长度的变量地址。
返回值:成功时返回文件描述符,失败时返回-1;

2.2 TCP客户端

2.2.1 调用socket函数生成“套接字”(相当于安装电话)

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

domain:套接字中的协议族(protocol family)。
type:套接字数据传输类型信息。
protocol:计算机间通信中使用的协议信息。
返回值:成功时返回文件描述符,失败时返回-1;

2.2.2 调用connect函数向服务器发送连接请求;

int connect(int sockfd, struct sockaddr* server_addr, socklen_t addrlen);

sockfd:客户端套接字文件描述符。
server_addr:服务端地址信息的变量地址。
addrlen:存有第二个变量地址长度的变量地址。
返回值:成功时返回0,失败时返回-1;

3、UDP

udp不同与tcp,udp是基于非连接的,udp不存在请求连接和接受连接过程,因此在这个意义上来讲udp无法明确的区分服务器和客户端,只是因为其提供服务而称为服务端。
一般情况来讲,tcp的可靠性高于udp,udp的传输速度快于tcp,但这并不绝对。

3.1 UDP相关函数

3.1.1 创建套接字

和TCP相同。

3.1.2 发送信息端函数

ssize_t sendto(int sockfd, void* buff, size_t nbytes, int flags, struct sockaddr* to, socklen_t addrlen);

sockfd:用于发送的套接字描述。
buff:保存待传输数据的缓冲地址;
nbytes:待传输的数据长度,以字节为单位。
flags:可选项参数,若没有则传递0。
to:目标地址的地址值。
addrlen:地址值结构体变量长度。
返回值:成功时返回传输的字节数,失败时返回-1。

ssize_t recvfrom(int sockfd, void* buff, size_t nbytes, int flags, struct sockaddr* from, socklen_t addrlen);

sockfd:用于接收的套接字描述。
buff:保存接收数据的缓冲地址;
nbytes:数据长度,以字节为单位。
flags:可选项参数,若没有则传递0。
from:发送端地址信息的地址值。
addrlen:发送端地址值结构体变量长度的变量地址值。
返回值:成功时返回传输的字节数,失败时返回-1。

4、Linux案例

4.1 TCP

TCP_server.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
  
int main(int argc, char *argv[])  
{  
    int server_sockfd;                //服务器端套接字  
    int client_sockfd;                 //客户端套接字  
    int len;  
    struct sockaddr_in my_addr;                 //服务器网络地址结构体  
    struct sockaddr_in remote_addr;         //客户端网络地址结构体  
    int 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(7088);        //服务器端口号  
      
    /*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/  
    //socket返回的值是一个文件描述符,SOCKET类型本身也是定义为int的
    //既然是文件描述符,那么在系统中都当作是文件来对待的,0,1,2分别表示标准输入、标准输出、标准错误。
    //所以其他打开的文件描述符都会大于2, 错误时就返回 -1. 这里INVALID_SOCKET 也被定义为 -1 
    if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)     // 无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误(-1)!
    {    
        perror("socket创建失败!");  
        return 1;  
    }  
   
        /*将套接字绑定到服务器的网络地址上*/  
    if (bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)  //如无错误发生,则bind()返回0。否则的话,将返回-1
    {  
        perror("bind失败!");  
        return 1;  
    }  
      
    /*监听连接请求--监听队列长度为5*/  
    listen(server_sockfd, 5);    //如无错误发生,则listen()返回0。否则的话,将返回-1
      
    sin_size=sizeof(struct sockaddr_in);  
      
    /*等待客户端连接请求到达*/  
    if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&remote_addr, &sin_size))<0)  //如无错误发生,则accept()返回0。否则的话,将返回-1
    {  
        perror("accept错误!");  
        return 1;  
    }  
    printf("accept client %s\n",inet_ntoa(remote_addr.sin_addr));  

    
    printf("成功建立连接!\n");
    len=send(client_sockfd, "welcome!\n", 22, 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");  
            return 1;  
        }  

    }  
    close(client_sockfd);  
    close(server_sockfd);  
    return 0;  
}

TCP_client.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>

  
int main(int argc, char *argv[])  
{  
    int client_sockfd;  
    int 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(7088);  //服务器端口号  
      
    /*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/  
    if((client_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)  
    {  
        perror("socket创建失败!");  
        return 1;  
    }  
      
    /*将套接字绑定到服务器的网络地址上*/  
    if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0)  
    {  
        perror("连接失败!");  
        return 1;  
    }  
    printf("connected to server\n");  
    len=recv(client_sockfd, buf, BUFSIZ, 0);  //接收服务器端信息 , recv函数第一个参数指定接收端套接字描述符;返回其实际copy的字节数。
    buf[len]='\0';  
    printf("from server: %s",buf); //打印服务器端信息  
      
    /*循环的发送接收信息并打印接收信息--recv返回接收到的字节数,send返回发送的字节数*/  
    while(1)  
    {  
        printf("Client enter string to send:");  
        scanf("%s", buf); 

        if(!strcmp(buf,"quit")){        // strcmp() 函数用于对两个字符串进行比较(区分大小写),如果返回值为0,说明比较的两个字符相等
            break;  
        }

        len=send(client_sockfd,buf,strlen(buf),0);   //如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR。
        len=recv(client_sockfd,buf,BUFSIZ,0);  
        buf[len]='\0';  
        printf("Server:%s\n",buf);  
    }  
    close(client_sockfd);  //关闭套接字  
    return 0;  
}  

4.2、UDP

UDP_server.c

#include <stdio.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>
#include <string.h>  
  
int main(int argc, char *argv[])  
{  
    int server_sockfd;  
    int len;  
    struct sockaddr_in my_addr;   //服务器网络地址结构体  
    struct sockaddr_in remote_addr; //客户端网络地址结构体  
    int 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");  
        return 1;  
    }  
   
    /*将套接字绑定到服务器的网络地址上*/  
    if (bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)  
    {  
        perror("bind");  
        return 1;  
    }  
    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");  
        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;  
} 

UDP_client.c

#include <stdio.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h> 
#include <string.h> 
  
int main(int argc, char *argv[])  
{  
    int client_sockfd;  
    int len;  
    struct sockaddr_in remote_addr; //服务器端网络地址结构体  
    int 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协议*/  
    client_sockfd=socket(PF_INET,SOCK_DGRAM,0);
    if(client_sockfd<0)  
    {    
        perror("socket");  
        return 1;  
    }  
    strcpy(buf,"This is a test message");  
    printf("sending: '%s'\n",buf);  
    sin_size=sizeof(struct sockaddr_in);  
      
    /*向服务器发送数据包*/  
    len=sendto(client_sockfd,buf,strlen(buf),0,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr));
    if(len < 0)  
    {  
        perror("recvfrom");   
        return 1;  
    }  
    close(client_sockfd);  
    return 0;  
}  
posted @ 2021-12-27 12:22  XiaoMu-1230  阅读(83)  评论(0编辑  收藏  举报