c++ 之 socket udp与tcp client server实现

这篇具有很好参考价值的文章主要介绍了c++ 之 socket udp与tcp client server实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

服务器开发系列



前言

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用打开open –> 读写write/read –> 关闭close模式来操作。Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).
说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。


一、socket是什么?

socket(套接字)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合

socket 是一种IPC方法,它允许位于同一主机(计算机)或使用网络连接起来的不同主机上的应用程序之间交换数据。

套接字描述符
其实就是一个整数,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr
c++ 之 socket udp与tcp client server实现
对于每个程序系统都有一张单独的表。精确地讲,系统为每个运行的进程维护一张单独的文件描述符表。当进程打开一个文件时,系统把一个指向此文件内部数据结构的指针写入文件描述符表,并把该表的索引值返回给调用者 。应用程序只需记住这个描述符,并在以后操作该文件时使用它。操作系统把该描述符作为索引访问进程描述符表,通过指针找到保存该文件所有的信息的数据结构。

套接字API里有个函数socket,它就是用来创建一个套接字。套接字设计的总体思路是,单个系统调用就可以创建任何套接字,因为套接字是相当笼统的。一旦套接字创建后,应用程序还需要调用其他函数来指定具体细节。
c++ 之 socket udp与tcp client server实现

文件描述符:
在linux系统中打开文件就会获得文件描述符,它是个很小的正整数。每个进程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是这个表的索引,每个表项都有一个指向已打开文件的指针。

文件指针:
C语言中使用文件指针做为I/O的句柄。文件指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个文件描述符。而文件描述符是文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄。

二、socket函数解析

socket常用函数:
参数具体含义可以在linux环境下man func

//tcp and udp
int socket(int domain, int type, int protocol)
int bind (int sockfd, const struct sockaddr * addr, socklen_t addrlen);
int listen(int sockfd, int backlog)

//only tcp
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

int shutdown(int sockfd,int howto); 
int close(int fd)

//tcp
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);
ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);

//udp
int sendto (socket s , const void * msg, int len, unsigned int flags,const struct sockaddr * to , int tolen );
int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen);  

//tcp and udp
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

//select
int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);

//epoll
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

socket()函数

int  socket(int protofamily, int type, int protocol);   //返回sockfd

protofamily:即协议域,又称为协议族(family)。常用的协议族有,AF_INET(IPV4)AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

bind()函数

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

sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。

addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。
ipv4对应的是:
struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

Unix域对应的是: 
#define UNIX_PATH_MAX    108
struct sockaddr_un { 
    sa_family_t sun_family;               /* AF_UNIX */ 
    char        sun_path[UNIX_PATH_MAX];  /* pathname */ 
};

addrlen:对应的是地址的长度。

listen()、connect()、accept()函数

int listen(int sockfd, int backlog);

listen函数的第一个参数即为要监听的socket描述字
第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

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

connect函数的第一个参数即为客户端的socket描述字
第二参数为服务器的socket地址
第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。

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

sockfd:套接口描述字,该套接口在listen()后监听连接。
addr:传出参数,返回链接客户端地址信息,含IP地址和端口号
addrlen:传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小

shutdown()、close()函数

int shutdown(int sockfd, int howto);   

sockfd是需要关闭的socket的描述符。
how允许为shutdown操作选择以下几种方式:
SHUT_RD:关闭连接的读端。也就是该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。进程将不能对该套接字发出任何读操作。对TCP套接字该调用之后接受到的任何数据将被确认然后无声的丢弃掉。
SHUT_WR:关闭连接的写端,进程不能在对此套接字发出写操作
SHUT_RDWR:相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR。使用close中止一个连接,但它只是减少描述符的参考数,并不直接关闭连接,只有当描述符的参考数为0时才关闭连接。
shutdown可直接关闭描述符,不考虑描述符的参考数,可选择中止一个方向的连接。

int close(int sockfd);   
调用close()函数来释放该socket,从而停止在该socket上的任何数据操作

select()函数

int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);

maxfd:  需要监视的最大的文件描述符的值+1 ;
rdset:  需要检测的可读文件描述符的集合;
wrset:  需要检测的可写文件描述符的集合;
exset:  需要检测的异常文件描述符的集合,不包括错误;
struct timeval: 用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0

fd_set的对象
FD_ZERO(fd_set *fdset) //将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset) //用于在文件描述符集合中增加一个新的文件描述符。
FD_CLR(fd_set *fdset) //用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset) //用于测试指定的文件描述符是否在该集合中。

struct timeval{
     __time_t tv_sec;        /* Seconds. */
     __suseconds_t tv_usec;  /* Microseconds. */
};

epoll()函数

int epoll_create(int size)

创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
第一个参数是epoll_create()的返回值。
第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd
第三个参数是需要监听的fd。
第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;
struct epoll_event {
    __uint32_t events; /* Epoll events */
    epoll_data_t data; /* User data variable */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。
maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,
参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。

Epoll的2种工作方式-水平触发(LT)和边缘触发(ET)

getsockopt()、setsockopt() 函数

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t*optlen);
sockfd:标识一个套接口的描述字。
level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
optname:需设置的选项。
optval:指针,指向存放选项待设置的新值的缓冲区。
optlen:optval缓冲区长度。

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
sockfd:标识一个套接口的描述字。
level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
optname:需设置的选项。
optval:指针,指向存放选项待设置的新值的缓冲区。
optlen:optval缓冲区长度。

其中optname:
SO_DEBUG,打开或关闭调试信息。
当option_value不等于0时,打开调试信息,否则,关闭调试信息。它实际所做的工作是在sock->sk->sk_flag中置 SOCK_DBG(10)位,或清SOCK_DBG位。
SO_REUSEADDR,打开或关闭地址复用功能。
当option_value不等于0时,打开,否则,关闭。它实际所做的工作是置sock->sk->sk_reuse为10。
SO_DONTROUTE,打开或关闭路由查找功能。
当option_value不等于0时,打开,否则,关闭。它实际所做的工作是在sock->sk->sk_flag中置或清SOCK_LOCALROUTE位。
SO_BROADCAST,允许或禁止发送广播数据。
当option_value不等于0时,允许,否则,禁止。它实际所做的工作是在sock->sk->sk_flag中置或清SOCK_BROADCAST位。
SO_SNDBUF,设置发送缓冲区的大小。
发送缓冲区的大小是有上下限的,其上限为256 * (sizeof(struct sk_buff) + 256),下限为2048字节。该操作将sock->sk->sk_sndbuf设置为val * 2,之所以要乘以2,是防止大数据量的发送,突然导致缓冲区溢出。最后,该操作完成后,因为对发送缓冲的大小 作了改变,要检查sleep队列,如果有进程正在等待写,将它们唤醒。
SO_RCVBUF,设置接收缓冲区的大小。
接收缓冲区大小的上下限分别是:256 * (sizeof(struct sk_buff) + 256)256字节。该操作将sock->sk->sk_rcvbuf设置为val * 2。
SO_KEEPALIVE,套接字保活。
如果协议是TCP,并且当前的套接字状态不是侦听(listen)或关闭(close),那么,当option_value不是零时,启用TCP保活定时 器,否则关闭保活定时器。对于所有协议,该操
作都会根据option_value置或清 sock->sk->sk_flag中的 SOCK_KEEPOPEN位。
SO_OOBINLINE,紧急数据放入普通数据流。
该操作根据option_value的值置或清sock->sk->sk_flag中的SOCK_URGINLINE位。
SO_NO_CHECK,打开或关闭校验和。
该操作根据option_value的值,设置sock->sk->sk_no_check。
SO_PRIORITY,设置在套接字发送的所有包的协议定义优先权。Linux通过这一值来排列网络队列。
这个值在06之间(包括06),由option_value指定。赋给sock->sk->sk_priority。
SO_LINGER,如果选择此选项, close或 shutdown将等到所有套接字里排队的消息成功发送或到达延迟时间后>才会返回. 否则, 调用将立即返回。
该选项的参数(option_value)是一个linger结构:
struct linger {
    int   l_onoff;   
    int   l_linger;  
};
如果linger.l_onoff值为0(关闭),则清 sock->sk->sk_flag中的SOCK_LINGER位;否则,置该位,并赋sk->sk_lingertime值为 linger.l_linger。

SO_PASSCRED,允许或禁止SCM_CREDENTIALS 控制消息的接收。
该选项根据option_value的值,清或置sock->sk->sk_flag中的SOCK_PASSCRED位。

SO_TIMESTAMP,打开或关闭数据报中的时间戳接收。
该选项根据option_value的值,清或置sock->sk->sk_flag中的SOCK_RCVTSTAMP位,如果打开,则还需设sock->sk->sk_flag中的SOCK_TIMESTAMP位,同时,将全局变量
netstamp_needed加1。

SO_RCVLOWAT,设置接收数据前的缓冲区内的最小字节数。
在Linux中,缓冲区内的最小字节数是固定的,为1。即将sock->sk->sk_rcvlowat固定赋值为1。

SO_RCVTIMEO,设置接收超时时间。
该选项最终将接收超时时间赋给sock->sk->sk_rcvtimeo。

SO_SNDTIMEO,设置发送超时时间。
该选项最终将发送超时时间赋给sock->sk->sk_sndtimeo。

SO_BINDTODEVICE,将套接字绑定到一个特定的设备上。
该选项最终将设备赋给sock->sk->sk_bound_dev_if。

SO_ATTACH_FILTER和SO_DETACH_FILTER。

三、UDP Client 与Server

c++ 之 socket udp与tcp client server实现

1.client

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

#define MAX_READ_LINE 1024

int udpclient(int port) {
    int ret = 0;
    const char *server_ip_addr = "127.0.0.1";
    int server_ip_port = port;
 
    //要发送给server的数据
    const char *send_message = "quit";

    //用于存储接收到的数据
    char buff[MAX_READ_LINE] = {0};

    int socket_fd = -1;
    int recv_len = -1;
    do{
        //创建client端的socket套接口
        socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (socket_fd < 0) {
            fprintf(stderr, "socket error %s errno: %d\n", strerror(errno), errno);
            ret = -1;
            break;
        }
    
        //初始化sockaddr_in结构体
        struct sockaddr_in u_sockaddr;
        memset(&u_sockaddr, 0, sizeof(struct sockaddr_in));
        u_sockaddr.sin_family = AF_INET;
        u_sockaddr.sin_port = htons(server_ip_port);
        inet_pton(AF_INET, server_ip_addr, &u_sockaddr.sin_addr);

        socklen_t server_len=sizeof(u_sockaddr);

        //向server发送数据
        if(sendto(socket_fd, send_message, strlen(send_message), 0, (struct sockaddr *) &u_sockaddr, sizeof(struct sockaddr_in)) < 0){
            fprintf(stderr, "send message error: %s errno : %d", strerror(errno), errno);
            ret = -1;
            break;
        }
        fprintf(stdout, "client----> udp client send msg to server: %s\n", send_message); 

        //取server发送数据
        recv_len = recvfrom(socket_fd, buff,MAX_READ_LINE, 0, (struct sockaddr *)&u_sockaddr, &server_len);

        buff[recv_len] = '\0';
        fprintf(stdout, "client----> udp client recv msg from server: %s\n", buff); 
    }while(false);

 
    //关闭套接字
    if (socket_fd >= 0){
        close(socket_fd);
    }
    socket_fd = -1;
 
    return ret;
}

2.server

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#define MAX_READ_LINE 1024

int udpserver(int port) {
    
    int ret = 0;
    int recv_len = -1;
    int server_ip_port = port;
 
    //用于存储接收到的数据
    char buff[MAX_READ_LINE] = {0};
 
    //初始化sockaddr_in结构体
    struct sockaddr_in u_sockaddr;
    memset(&u_sockaddr, 0, sizeof(u_sockaddr));      //bzero(&u_sockaddr,sizeof(u_sockaddr));
    u_sockaddr.sin_family = AF_INET;
    u_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    u_sockaddr.sin_port = htons(server_ip_port);

    socklen_t socklen = sizeof(u_sockaddr);
    //创建server端的socket套接字
    int server_fd = -1;
    do{
        server_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (server_fd < 0) {
            fprintf(stderr, "socket error %s errno: %d\n", strerror(errno), errno);
            ret = -1;
            break;
        }
    
        //绑定
        ret = bind(server_fd,(struct sockaddr *) &u_sockaddr,sizeof(u_sockaddr));
        if (ret < 0) {
            fprintf(stderr, "bind socket error %s errno: %d\n", strerror(errno), errno);
            ret = -1;
            break;
        }
    
        //接收来自客户端的链接请求
        for(;;) {
            //读取客户端数据到buff中
            recv_len = recvfrom(server_fd, buff, MAX_READ_LINE, 0, (struct sockaddr *)&u_sockaddr, &socklen);
            if (recv_len < 0) {
                fprintf(stderr, "recv error %s errno: %d\n", strerror(errno), errno);
                continue;
            }
    
            buff[recv_len] = '\0';
            //将接收到的数据标准输出
            fprintf(stdout, "server----> udp server recv msg from client: %s\n", buff);

            //将数据发送到客户端
            sendto(server_fd, buff,MAX_READ_LINE, 0, (struct sockaddr *)&u_sockaddr, socklen);
            fprintf(stdout, "server----> udp server send msg to client: %s\n", buff);

            //解决tcp服务不退出的问题
            if(strcmp(buff,"quit") == 0){
                fprintf(stdout, "server----> udp server will quit\n");
                break;
            }
        }
    }while(false);
 
    //关闭套接字
    if (server_fd >= 0){
        close(server_fd);
    }
    server_fd = -1;
 
    return 0;
}

四、TCP Client 与Server

c++ 之 socket udp与tcp client server实现

1.client

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

#define MAX_READ_LINE 1024
int tcpclient(int port) {
    int ret = 0;
    const char *server_ip_addr = "127.0.0.1";
    int server_ip_port = port;
 
    //要发送给server的数据
    const char *send_message = "quit";

    //用于存储接收到的数据
    char buff[MAX_READ_LINE] = {0};

    int socket_fd = -1;
    int recv_len = -1;
    do{
        //创建client端的socket套接口
        socket_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (socket_fd < 0) {
            fprintf(stderr, "socket error %s errno: %d\n", strerror(errno), errno);
            ret = -1;
            break;
        }
    
        //初始化sockaddr_in结构体
        struct sockaddr_in t_sockaddr;
        memset(&t_sockaddr, 0, sizeof(struct sockaddr_in));
        t_sockaddr.sin_family = AF_INET;
        t_sockaddr.sin_port = htons(server_ip_port);
        inet_pton(AF_INET, server_ip_addr, &t_sockaddr.sin_addr);
    
        //连接
        if((connect(socket_fd, (struct sockaddr*)&t_sockaddr, sizeof(struct sockaddr))) < 0 ) {
            fprintf(stderr, "connect error %s errno: %d\n", strerror(errno), errno);
            ret = -1;
            break;
        }
    
        //向server发送数据
        if((send(socket_fd, send_message, strlen(send_message), 0)) < 0) {
            fprintf(stderr, "send message error: %s errno : %d", strerror(errno), errno);
            ret = -1;
            break;
        }
        fprintf(stdout, "client----> tcp client send msg to server: %s\n", send_message); 

        //取server发送数据
        recv_len = recv(socket_fd,buff,MAX_READ_LINE,0);
        buff[recv_len] = '\0';
        fprintf(stdout, "client----> tcp client recv msg from server: %s\n", buff); 
    }while(false);
 
    //关闭套接字
    if (socket_fd >= 0){
        close(socket_fd);
    }
    socket_fd = -1;
 
    return ret;
}

2.server

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
 
#define MAX_READ_LINE 1024
 
int tcpserver(int port) {
    int recv_len = -1;
    int conn_fd = -1;
    int ret = 0;
 
    int server_ip_port = port;
 
    //用于存储接收到的数据
    char buff[MAX_READ_LINE] = {0};
 
    //初始化sockaddr_in结构体
    struct sockaddr_in t_sockaddr;
    memset(&t_sockaddr, 0, sizeof(t_sockaddr));
    t_sockaddr.sin_family = AF_INET;
    t_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    t_sockaddr.sin_port = htons(server_ip_port);
 
    //创建server端的socket套接字
    int listen_fd = -1;
    do{
        listen_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_fd < 0) {
            fprintf(stderr, "socket error %s errno: %d\n", strerror(errno), errno);
            ret = -1;
            break;
        }
    
        //绑定
        ret = bind(listen_fd,(struct sockaddr *) &t_sockaddr,sizeof(t_sockaddr));
        if (ret < 0) {
            fprintf(stderr, "bind socket error %s errno: %d\n", strerror(errno), errno);
            ret = -1;
            break;
        }
    
        //监听
        ret = listen(listen_fd, 1024);
        if (ret < 0) {
            fprintf(stderr, "listen error %s errno: %d\n", strerror(errno), errno);
            ret = -1;            
            break;
        }
    
        //接收来自客户端的链接请求
        for(;;) {
            conn_fd = accept(listen_fd, (struct sockaddr*)NULL, NULL);
            if(conn_fd < 0) {
                fprintf(stderr, "accpet socket error: %s errno :%d\n", strerror(errno), errno);
                continue;
            }
    
            //读取数据到buff中
            recv_len = recv(conn_fd, buff, MAX_READ_LINE, 0);
            if (recv_len < 0) {
                fprintf(stderr, "recv error %s errno: %d\n", strerror(errno), errno);
                continue;
            }
    
            buff[recv_len] = '\0';
            //将接收到的数据标准输出
            fprintf(stdout, "server----> tcp server recv msg from client: %s\n", buff);

            //将接收到的数据发送到客户端
            send(conn_fd,(void*)buff,MAX_READ_LINE,0);
            fprintf(stdout, "server----> tcp server send msg to client: %s\n", buff);

            close(conn_fd);
            conn_fd = -1;

            //解决tcp服务不退出的问题
            if(strcmp(buff,"quit") == 0){
                fprintf(stdout, "server----> tcp server will quit\n");
                break;
            }
        }
    }while(false);
    buff[0] = '\0';
    //关闭套接字
    if (listen_fd >= 0){
        close(listen_fd);
    }
    listen_fd = -1;
 
    return 0;
}

总结

通过上面的例子,应该对普通socket模型有了基本的理解,不过对与tcp udp编程来说,就是个皮毛。文章来源地址https://www.toymoban.com/news/detail-413739.html

到了这里,关于c++ 之 socket udp与tcp client server实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python socket编程7 - 使用PyQt6 开发UI界面新增实现UDP server和client单机通讯的例子

    在第五篇中,简单实现了命令行下的 TCP/UDP server和client的单机通讯。 在第六篇中,实现了PyQt6开发界面,TCP协议实现的单机server和client的通讯功能。 这一篇,在第六篇的基础上,增加了UDP server和client的单机通讯功能。 1、UDP Server 界面实现服务配置和数据提供 2、UDP Server封装

    2024年02月19日
    浏览(46)
  • 基于Springboot整合Socket仿微信实现群聊、私聊功能。实现客户端client,服务端server心跳维护、超时机制【一文通】

    博主介绍: ✌java资深开发工程师、Java领域优质创作者,博客之星、专注于Java技术领域和学生毕业项目实战,面试讲解跟进,高校老师/讲师/同行交流合作✌ 胡广愿景: \\\"比特星球\\\",致力于帮助底层人员找到工作, 让每个底层人员都能找到属于自己的星球。 拓展学习领域,获

    2024年02月19日
    浏览(51)
  • 【Java网络编程】基于UDP-Socket 实现客户端、服务器通信

    ​ 哈喽,大家好~我是你们的老朋友: 保护小周ღ   本期为大家带来的是网络编程的 UDP Socket 套接字,基于 UDP协议的 Socket 实现客户端服务器通信 ,Socket 套接字可以理解为是,传输层给应用层提供的一组 API,如此程序,确定不来看看嘛~~ 本期收录于博主的专栏 : JavaEE_保

    2024年02月02日
    浏览(63)
  • 使用Socket实现TCP版的回显服务器

    Socket(Java套接字)是Java编程语言提供的一组类和接口,用于实现网络通信。它基于Socket编程接口,提供了一种简单而强大的方式来实现网络应用程序。 Socket类库提供了丰富的方法和功能,用于处理网络通信的各个方面。它支持TCP和UDP协议,可以实现可靠的、面向连接的通信

    2024年02月14日
    浏览(46)
  • 【Java】网络编程与Socket套接字、UDP编程和TCP编程实现客户端和服务端通信

    为什么需要网络编程? 现在网络普及程序越来越高,网络上保存着我们日常生活中需要的各种资源,使用程序通过网络来获取这些资源的过程就需要网络编程来实现。 什么是网络编程? 网络编程,指网络上的主机,通过不同的进程以程序的方式实现网络通信(网络数据传输)

    2024年02月17日
    浏览(73)
  • 【网络编程】实现UDP/TCP客户端、服务器

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、UDP 1、Linux客户端、服务器 1.1udpServer.hpp 1.2udpServer.cc 1.3udpClient.hpp 1.4udpClient.cc 1.5onlineUser.hpp 2、Windows客户端 二、T

    2024年02月06日
    浏览(57)
  • Socket网络编程(TCP/IP)实现服务器/客户端通信。

    一.前言 回顾之前进程间通信(无名管道,有名管道,消息队列,共享内存,信号,信号量),都是在同一主机由内核来完成的通信。 那不同主机间该怎么通信呢? 可以使用Socket编程来实现。 Socket编程可以通过网络来实现实现不同主机之间的通讯。 二.Socket编程的网络模型如

    2024年02月08日
    浏览(83)
  • C# Socket通信从入门到精通(16)——单个同步UDP服务器监听多个客户端C#代码实现

    我们在开发UDP通信程序时,有时候我们也需要开发UDP服务器程序,这个服务器只需要和一个客户端实现通信,比如这篇博文C# Socket通信从入门到精通(15)——单个同步UDP服务器监听一个客户端C#代码实现,但是在实际项目中有的时候需要和多个客户端进行通信,这时和一个客

    2024年01月22日
    浏览(58)
  • C# Socket通信从入门到精通(15)——单个同步UDP服务器监听一个客户端C#代码实现

    我们在开发UDP通信程序时,除了开发UDP客户端程序,有时候我们也需要开发UDP服务器程序,这在实际项目中是经常会遇到的,所以说掌握UDP服务器程序的开发是一项必备的技能,尤其在上位机软件开发领域,掌握UDP服务器程序的开发是走向高级工程师的必经之路,也是面试必

    2024年02月03日
    浏览(54)
  • unreal engine c++ 创建tcp server, tcp client

    TCP客户端 TcpConnect.h TcpConnect.cpp tcp server cpp FTcpServerReceive h cpp 在 GameInstance 使用

    2024年02月14日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包