【Linux】网络编程-UDP和TCP的socket编程

网络套接字socket

port端口号

本质:端口号是一个2字节16位的整数,范围在[0,65535];

作用:端口号用来表示一个进程,告诉操作系统,当前这个数据要交给哪一个进程来处理,一个端口号只能被一个进程所占用,一个进程可以占用多个端口号(前提是在同一台机器当中)。

知名端口:1 ~ 1034 (轻易不要绑定)
Oracle:1521 ; MySQL:3306;
http协议:使用的是80端口;https协议:使用的是443端口。

网络数据的五元组信息(源IP、源端口、目的IP、目的端口、协议)

源IP地址:标识网络数据从哪台主机发出;

源端口:标识网络数据从“源IP”对应这台主机的哪个进程;

目的IP:标识网络数据要去往哪一台机器;

目的端口:当通过目的IP,找到目的主机之后,通过目的端口找到对应进程;

协议:双方传输数据的时候,使用什么协议(一般指UDP\TCP)。

在网络当中传输的数据,都是包含五元信息组的。

认识UDP\TCP

TCP

TCP(Transmission Control Protocol)传输控制协议:

  • 传输层协议

  • 有连接:TCP双方在发送数据之前会先建立连接(1、确保对方能正常通信;2、沟通双方发送后续数据的细节)。

  • 可靠传输:TCP保证传输的数据是可靠的、有序的到达对端。

  • 面向字节流:1、对于传输的数据没有明显的边界;2、对于接收方而言,可以按照任意的字节进行接收。

UDP

UDO(User Daragram Protocol)用户数据报协议

  • 传输层协议

  • 无连接:UDP双方在发送数据之前,是不需要进行沟通的,只需要知道对方的IP和端口就好(可能对方进程还没准备好),就可以发送。

  • 不可靠传输:不保证UDP的数据是可靠,有序的到达对方。

  • 面向数据报:UDP和应用层/网络层递交数据的时候,都是整条数据进行交互的

网络字节序

我们知道,内存中的多字节数据对于内存地址有着大小端之分,磁盘文件中的多字节数据相对于文件中的个偏移地址也有大小端之分,网络数据流同样有大小端之分,那么如何定义网络数据流的地址:

  • 发送主句通常将发送缓冲区的数据按内存地址从低到高的顺序发出;

  • 接收主机把从网络上接收到的字节序依次保存在接收缓冲区,也是按照内存地址从低到高保存;

  • 因此网络数据流的地址应该这样规定:先发出的数据是低地址,后发出的数据是高地址;

  • TCP/IP协议规定,网络数据流应该采用大端字节序,即低地址高字节;

  • 不管这台主机是大端还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据。

  • 如果当前发送的主机是小端,就需要先将数据转成大端,否则就忽略直接发送就行。

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

#include<arpea/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • 这些函数名字好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数;

  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送;

  • 如果主机是小端字节序,这些函数将参数做相应的大小转换然后返回;

  • 如果诸暨市大端字节序,这些函数不做转换,将参数原封不动的返回。

socket(套接字)编程接口

socket常见API

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);

// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);

// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);

sockaddr结构

socket API是一层抽象的w网络编程接口,适用于各种底层网络协议,比如:IPv4、IPv6以及UNIX Domain Socket,然而各种网络协议的地址格式并不相同。

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址;

  • IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容;

  • socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。

sockaddr结构

sockaddr_in结构

虽然socket api的接口是sockaddr,但是我们真正在基于IPv4编程时,使用的数据结构是sockaddr_in;这个结构里主要由三部分信息:地址类型、端口号、IP地址;

in_addr结构

in_addr用来表示一个IPv4的IP地址其实就是一个322位的整数。

封装UdpSocket

udp_socket.hpp

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cassert>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
class UdpSocket {
public:
 UdpSocket() : fd_(-1) {
}
 bool Socket() {
 fd_ = socket(AF_INET, SOCK_DGRAM, 0);
 if (fd_ < 0) {
 perror("socket");
 return false;
 }
 return true;
 }
 bool Close() {
 close(fd_);
 return true;
 }
 bool Bind(const std::string& ip, uint16_t port) {
 sockaddr_in addr;
 addr.sin_family = AF_INET;
 addr.sin_addr.s_addr = inet_addr(ip.c_str());
 addr.sin_port = htons(port);
 int ret = bind(fd_, (sockaddr*)&addr, sizeof(addr));
 if (ret < 0) {
 perror("bind");
 return false;
 }
 return true;
 }
 bool RecvFrom(std::string* buf, std::string* ip = NULL, uint16_t* port = NULL) {
 char tmp[1024 * 10] = {0};
 sockaddr_in peer;
 socklen_t len = sizeof(peer);
 ssize_t read_size = recvfrom(fd_, tmp,
 sizeof(tmp) - 1, 0, (sockaddr*)&peer, &len);
 if (read_size < 0) {
 perror("recvfrom");
 return false;
 }
 // 将读到的缓冲区内容放到输出参数中
 buf->assign(tmp, read_size);
 if (ip != NULL) {
 *ip = inet_ntoa(peer.sin_addr);
 }
 if (port != NULL) {
 *port = ntohs(peer.sin_port);
 }
 return true;
 }
 bool SendTo(const std::string& buf, const std::string& ip, uint16_t port) {
 sockaddr_in addr;
 addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str());
 addr.sin_port = htons(port);
 ssize_t write_size = sendto(fd_, buf.data(), buf.size(), 0, (sockaddr*)&addr, sizeof(addr));
 if (write_size < 0) {
 perror("sendto");
 return false;
 }
 return true;
 }
private:
 int fd_;
};

UDP-socket(套接字)编程

编程流程

准备工作:创建套接字、绑定地址信息。

创建套接字的含义:将进程和网卡进行绑定,进程可以从网络协议栈当中接收或者发送数据。

绑定地址信息的含义:给进程绑定IP,绑定端口。为了在网络当中标识一台主机和一个进程。

编程接口

创建套接字接口:

#inlcude<sys/socket.h>
int socket(int domain,int type,int protocol);

//domain:地质域--选择一个具体协议组进行沟通,对于我们而言,udp/tcp可以认为在指定网络层使用什么协议。
//    AF_UNIX:本地域套接字(在同一台机器使用文件进行通信,不用跨机器)
//    AF_INET: ipv4版本的ip协议
//    AF_INET6:ipv6版本的ip协议
//    **因为IPv4版本IP协议对应IP地址不够用,导致两个IP协议版本,IPv4和IPv6本质区别在于IP地址不一样。
 
//type:套接字的类型
//    SOCK_DGRAM: 用户数据报套接字--对应UDP
//    SOCK_STREAM:流式套接字--对应TCP
 
//protocol:协议
//    0:标识按照套接字类型选择默认协议
//    IPPROTO_TCP(6): 代表TCP协议
//    IPPROTO_UDP(17):代表UDP协议
 
//返回值:返回套接字操作句柄,本质上就是一个文件描述符
//    创建成功:大于等于0;
//    创建失败:小于0。

绑定接口:

int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
 
//sockfd:  套接字描述符
//addr:    绑定的地址信息
//adderlen:绑定的地址信息长度

发送接口:

ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr *dest_addr,socklen_t addrlen);

//sockfd:   套接字描述符
//buf:      要发送的数据
//len:      发送的数据的长度
//flags:    0(阻塞发送)
//dest_addr:对端地址信息结构,包含对端主机的IP,端口
//adderlen: 对端地址信息结构长度
 
//返回值:真正发送的数据长度,单位字节

接收接口:

ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen);
 
//参数:
 
//sockfd:套接字描述符
 
//buf:接收到的数据保存到buf指向的空间当中,buf指向的空间需要提前开辟
 
//len:最多可以接受多少数据
 
//flags:0(阻塞接收)
 
//src_addr:接收到的数据从哪里来,对端的地址信息结构
     //客户端调用,接收到的就是服务端的地址信息结构
     //服务端调用,接收到的就是客户端的地址信息结构
 
//addrlen:绑定的地址信息长度
 
//返回值:
 
//真正接收到的数据长度,单位字节
————————————————
版权声明:本文为CSDN博主「伍伍1GB」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_59824611/article/details/128451970

关闭套接字接口:

int close(int fd);
//fd:套接字接口。

UDP编程代码

用C语言简单实现服务端客户端收发消息:

客户端:

#include<stdio.h>
  2 #include<iostream>
  3 #include<unistd.h>
  4 #include<string.h>
  5 
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 
 10 int main()
 11 {
 12   int sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
 13   if(sockfd < 0)
 14   {
 15     std::cout<<"sockfd";
 16     return 0;                                                 
 17   }
 18 
 19   struct sockaddr_in addr;
 20   addr.sin_family = AF_INET;
 21   // 这里填充的是服务端的端口, 因为数据是发送到服务端的
 22   addr.sin_port = htons(5555);
 23   // 这里填充的是服务端的ip
 24   addr.sin_addr.s_addr = inet_addr("0.0.0.0");
 25 
 26   while(1)
 27   {
 28     char buf[1024] = {0};
 29     std::cout<<"please enter msg: ";
 30     fflush(stdout);
 31     std::cin >> buf;
 32     sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr));
 33     //相当于不要recvfrom函数返回服务端的地址信息,以及地址信息的长度, 因为客户端是清楚服务端的地址信息的
 34     memset(buf, '\0', sizeof(buf));
 35     recvfrom(sockfd, buf, sizeof(buf) - 1, 0, NULL, NULL);
 36     printf("[buf is] %s\n", buf);
 37     sleep(1); 
 38   }
 39   close(sockfd);
 40   return 0;                                                   
 41 }

服务端:

  1 #include<sys/socket.h>
  2 #include<netinet/in.h>
  3 #include<arpa/inet.h>
  4 #include<stdio.h>
  5 #include<iostream>
  6 #include<unistd.h>
  7 #include<string.h>
  8 
  9 int main()
 10 {
 11    /*
 12      * 1.创建套接字
 13      * 2.绑定地址信息
 14      * 3.收发数据
 15      * 4.关闭套接字
 16      * */                                                     
 17 
 18   int sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
 19   if(sockfd < 0){
 20     std::cout<<"socket";
 21     return 0;
 22   }
 23 
 24   //addr:服务端绑定的地址信息结构, 保存着服务端需要绑定的ip地址和端口
 25   struct sockaddr_in addr;
 26   //填充地址类型, 后续bind函数解析了地址类型之后, 就知道传入的是哪一个数据结构了
 27   addr.sin_family = AF_INET;
 28   //填充端口, 一定要注意将主机字节序转换为网络字节序
 29   addr.sin_port = htons(5555);
 30   //填充IP地址
 31   
 32   addr.sin_addr.s_addr = inet_addr("0.0.0.0");
 33 
 34   int ret = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
 35   if(ret < 0){
 36     std::cout<<"bind";
 37     return 0;
 38   }
 39   //能接收,但是没有给客户端进行响应
 40   while(1){
 41     //接收
 42     
 43     char buf[1024] = {0};
 44     struct sockaddr_in peer_addr;
 45     socklen_t peer_addrlen = sizeof(peer_addr);
 46     recvfrom(sockfd,buf,sizeof(buf)-1,0,(struct sockaddr*)&peer_addr,&peer_addrlen);
 47     printf("[buf is]:%s from %s,%d\n",buf,inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
 48 
 49     //发送
 50     
 51     memset(buf,'\0',sizeof(buf));
 52     std::cout<<"please enter msg:";
 53     fflush(stdout);
 54     std::cin>>buf;
 55     sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&peer_addr,sizeof(peer_addr));
 56   }
 57   close(sockfd);
 58   return 0;
 59 }  

TCP-socket(套接字)编程

编程流程

编程接口

监听接口

int listen(int sockfd,int backlog);
//sockfd:套接字描述符,socket函数创建的侦听套接字
//backlog:TCP开发连接数(已完成连接的大小);tcp的服务端在一瞬间并发能够处理的最大的tcp的连接数量。强调:并不是ycp服务端最多能够接受的tcp数量!

连接接口

int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
//sockfd:套接字描述符
//addr:地址信息结构,描述服务端地址信息(服务端的ip和端口)
//addelen:地址信息长度
//返回值:成功返回0;失败返回-1.
//注意:该函数不仅可以完成连接功能,如果客户端没有绑定,同时也会进行绑定客户端的地址信息。

接收新连接

int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
//sockfd:套接字描述符,监听套接字。
//addr:地址信息结构体,描述客户端地址信息的结构体。
//addrlen:地址信息长度。
//返回值:成功返回新连接的套接字;失败返回-1。
//注意:这个是阻塞调用的函数。如果已完成队列当中没回应已经建立连接的连接则阻塞,如果有就返回,

接收成功标志:服务端为该链接创建了套接字描述符

发送接口

ssize_t send(int sockfd,const void *buf,size_t len,int flags);
//sockfd:发送buf指向的空间的内容
//buf:发送buf指向的空间的内容
//len:数据长度
//flags:0(阻塞发送)
//返回值:成功返回发送的字节数量;失败返回-1.

接收接口

ssize_t recv(int sockfd,void *buf,size_t len,int flags);
//sockfs:套接字描述符(客户端调用recv,使用客户端调用的套接字;服务端调用recv,使用的就是新连接的套接字描述符)
//buf:将接收到的数据存在buf指定的空间、空间需要提前开辟
//len:数据长度
//flags:0(阻塞发送)
//返回值:成功返回接收的字节数量;对端关闭连接返回0;接受错误返回-1.

TCP编程代码

模拟实现一个收发信息的两端:

客户端:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
 
 
int main(){
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("socket");
        return 0;
    }
 
    //int connect(int sockfd, const struct sockaddr *addr,
    //                socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = inet_addr("120.78.126.148");
    int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("connect");
        return 0;
    }
 
    while(1){
        printf("please enter your msg: ");
        fflush(stdout);
        char buf[1024] = {0};
        std::cin >> buf;
        send(sockfd, buf, strlen(buf), 0);
 
 
        memset(buf, '\0', sizeof(buf));
        ssize_t r_size = recv(sockfd, buf, sizeof(buf) - 1, 0);
        if(r_size < 0){
            perror("recv");
            continue;
        }else if(r_size == 0){
            // server shutdown
            break;
        }else{
            printf("%s\n", buf);
        }
    }
    close(sockfd);
    return 0;
}

服务端:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
 
 
int main(){
    /*1.创建套接字(流式套接字)*/
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }
 
    /*2.绑定地址信息, 服务端绑定ip和端口*/
    //int bind(int sockfd, const struct sockaddr *addr,
    //                                              socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    //当前机器的私网ip地址(自己机器网卡的ip地址), 如果说要绑定, 则绑定该ip地址: 172.28.71.17
    //当前机器的公网IP地址(云服务器厂商为我们提供的ip地址, 从公网访问公网ip地址则能放到该机器):120.78.126.148
    //    !!!不要绑定公网ip地址, 因为公网IP地址不是当前机器的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }
 
    /*3.监听, 当调用了监听接口之后, 服务端就能接收客户端的连接请求了*/
    ret = listen(listen_sockfd, 2);
    if(ret < 0){
        perror("listen");
        return 0;
    }
 
    while(1){
    /*4.接收新连接, 这个函数是阻塞调用的函数*/
    int new_sockfd = accept(listen_sockfd, NULL, NULL);
    if(new_sockfd < 0){
        perror("accept");
        return 0;
    }
 
        char buf[1024] = {0};
        ssize_t r_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);
        if(r_size < 0){
            perror("recv");
            continue;
        }else if(r_size == 0){
            // peer shutdown
            printf("client shutdown connection\n");
            close(new_sockfd);
            break;
        }else{
            printf("[buf is ] : %s\n", buf);
        }
 
        memset(buf, '\0', sizeof(buf));
        printf("please enter msg: ");
        fflush(stdout);
        std::cin >> buf;
        send(new_sockfd, buf, strlen(buf), 0);
    }
    
    close(listen_sockfd);
    return 0;
}

问题来了,如果多个客户端请求连接服务端怎么办?

上面的代码显示如果监听到了连接请求就进入循环,然后接收,但是并不能出循环啊。

把监听端口放入循环里吗?

还是别的办法?

由此我们引出了对于TCP编程与多线程/多进程之间的结合运用

TCP多线程/多进程结合编程

服务端多线程:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <iostream>
 
struct NewSockfd{
    int new_sockfd;
};
 
 
void* worker_thread(void* arg){
    pthread_detach(pthread_self());
    struct NewSockfd* ns = (struct NewSockfd*)arg;
 
    while(1){
        char buf[1024] = {0};
        ssize_t r_size = recv(ns->new_sockfd, buf, sizeof(buf) - 1, 0);
        if(r_size < 0){
            perror("recv");
            continue;
        }else if(r_size == 0){
            // peer shutdown
            printf("client shutdown connection\n");
            close(ns->new_sockfd);
            break;
        }else{
            printf("[buf is ] : %s\n", buf);
        }
 
        memset(buf, '\0', sizeof(buf));
        printf("please enter msg: ");
        fflush(stdout);
        std::cin >> buf;
        send(ns->new_sockfd, buf, strlen(buf), 0);
 
    }
 
    delete ns;
    return NULL;
}
 
int main(){
    /*1.创建套接字(流式套接字)*/
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }
 
    /*2.绑定地址信息, 服务端绑定ip和端口*/
    //int bind(int sockfd, const struct sockaddr *addr,
    //                                              socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    //当前机器的私网ip地址(自己机器网卡的ip地址), 如果说要绑定, 则绑定该ip地址: 172.28.71.17
    //当前机器的公网IP地址(云服务器厂商为我们提供的ip地址, 从公网访问公网ip地址则能放到该机器):120.78.126.148
    //    !!!不要绑定公网ip地址, 因为公网IP地址不是当前机器的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }
 
    /*3.监听, 当调用了监听接口之后, 服务端就能接收客户端的连接请求了*/
    ret = listen(listen_sockfd, 2);
    if(ret < 0){
        perror("listen");
        return 0;
    }
 
    while(1){
        /*4.接收新连接, 这个函数是阻塞调用的函数*/
        int new_sockfd = accept(listen_sockfd, NULL, NULL);
        if(new_sockfd < 0){
            perror("accept");
            return 0;
        }
 
        struct NewSockfd * ns = new struct NewSockfd;
        ns->new_sockfd = new_sockfd;
        pthread_t tid;
        int ret = pthread_create(&tid, NULL, worker_thread, (void*)ns);
        if(ret < 0){
            close(new_sockfd);
            continue;
        }
    }
 
    close(listen_sockfd);
    return 0;
}

服务端多进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
 
void sigcallback(int signo){
    wait(NULL);
}
 
int main(){
    signal(SIGCHLD, sigcallback);
 
    /*1.创建套接字(流式套接字)*/
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }
 
    /*2.绑定地址信息, 服务端绑定ip和端口*/
    //int bind(int sockfd, const struct sockaddr *addr,
    //                                              socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    //当前机器的私网ip地址(自己机器网卡的ip地址), 如果说要绑定, 则绑定该ip地址: 172.28.71.17
    //当前机器的公网IP地址(云服务器厂商为我们提供的ip地址, 从公网访问公网ip地址则能放到该机器):120.78.126.148
    //    !!!不要绑定公网ip地址, 因为公网IP地址不是当前机器的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }
 
    /*3.监听, 当调用了监听接口之后, 服务端就能接收客户端的连接请求了*/
    ret = listen(listen_sockfd, 2);
    if(ret < 0){
        perror("listen");
        return 0;
    }
 
    while(1){
        /*4.接收新连接, 这个函数是阻塞调用的函数*/
        int new_sockfd = accept(listen_sockfd, NULL, NULL);
        if(new_sockfd < 0){
            perror("accept");
            return 0;
        }
 
        int pid = fork();
        if(pid < 0){
            close(new_sockfd);
            continue;
        }else if(pid == 0){
            while(1){
                char buf[1024] = {0};
                ssize_t r_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);
                if(r_size < 0){
                    perror("recv");
                    continue;
                }else if(r_size == 0){
                    // peer shutdown
                    printf("client shutdown connection\n");
                    close(new_sockfd);
                    exit(1);
                }else{
                    printf("[buf is ] : %s\n", buf);
                }
 
                memset(buf, '\0', sizeof(buf));
                printf("please enter msg: ");
                fflush(stdout);
                std::cin >> buf;
                send(new_sockfd, buf, strlen(buf), 0);
            }
        }else{
            //wait(NULL);
        }
 
    }
 
    close(listen_sockfd);
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux网络编程中的TCPUDP是两种常见的传输协议。 TCP(Transmission Control Protocol)是一种基于连接的可靠传输协议。它提供了面向连接、可靠的数据传输服务。在TCP通信中,数据被分割成小的数据块,通过TCP连接按序传输,并且保证数据的可靠性,即使在网络拥塞或数据丢失的情况下也能重新传输丢失的数据。TCP适用于对可靠性要求较高的应用程序,如文件传输、电子邮件和网页浏览。 UDP(User Datagram Protocol)是一种无连接的不可靠传输协议。它提供了一种无序、不可靠的数据传输服务。在UDP通信中,数据以数据包(也称为数据报)的形式发送,不进行连接建立和断开,也不保证数据的可靠性和按序传输。UDP适用于对实时性要求较高、对数据可靠性要求较低的应用程序,如音视频流媒体、在线游戏等。 在Linux中进行TCPUDP网络编程可以使用Socket API。该API提供了一组函数和数据结构,用于创建套接字(socket)、绑定(bind)套接字到特定的IP地址和端口、监听(listen)连接请求、接受(accept)连接、建立连接(connect)、发送(send)和接收(receive)数据等操作。 你可以使用C语言或其他支持Socket API的编程语言来进行Linux网络编程,通过调用Socket API提供的函数来实现TCPUDP通信。在编程过程中,你需要了解TCPUDP的特点、使用套接字创建相应的连接类型、发送和接收数据的方式等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值