Linux下面向TCP连接的C++ Socket编程实例

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。即Socket提供了操作上述特殊文件的接口,使用这些接口可以实现网络编程。

Linux下面向TCP连接的C++ Socket编程实例

Socket通信流程图

TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议,在正式通信之前必须建立起连接。UDP(User Data Protocol,用户数据报协议)是一个非连接的协议。因此TCP的服务器模式比UDP的服务器模式多了listen,accept函数。TCP客户端比UDP客户端多了connect函数。这里着重介绍TCP下socket简单编程。

一、TCP使用Socket创建客户端

(1)创建一个socket,用函数socket();

导入需要的头文件:

#include <sys/types.h>
#include <sys/socket.h>

声明

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

参数说明:

1)domain

AF_UNIX, AF_LOCAL :Local communication

AF_INET:IPv4 Internet protocols

AF_INET6:IPv6 Internet protocols

AF_IPX:IPX - Novell protocols

AF_NETLINK:Kernel user interface device

AF_X25:ITU-T X.25 / ISO-8208 protocol

AF_AX25:Amateur radio AX.25 protocol

AF_ATMPVC:Access to raw ATM PVCs

AF_APPLETALK:AppleTalk

AF_PACKET:Low level packet interface

AF_ALG:Interface to kernel crypto API

2)type

SOCK_SEQPACKET:为固定最大长度的数据报提供有序的、可靠的、基于双向连接的数据传输路径; 请求者需要在每次输入系统调用时读取整个数据包。

SOCK_RAW:提供原始网络协议访问。

SOCK_RDM:提供不保证排序的可靠数据报层。

SOCK_PACKET:已过时,不应在新程序中使用。

3)protocol

IPPROTO_TCP:TCP传输协议

IPPTOTO_UDP:UDP传输协议

IPPROTO_SCTP:STCP传输协议

IPPROTO_TIPC:TIPC传输协议

4)返回值类型:

创建成功,返回新的Socket对象;创建错误,将返回-1。

(2)连接服务器,用函数connect();

声明:

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

参数说明:

1)sockfd:

2)addr:连接目标服务器的协议族;

3)addrlen:对应的地址长度。

(3)收发数据,用函数send()和recv(),或者read()和write();

不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。

客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。

声明:

int send(SOCKET s, const char FAR *buf, int len, int flags);

参数说明:

1)s:指定发送端套接字描述符;

2)buf:指明一个存放应用程序要发送数据的缓冲区;

3)len:指明实际要发送的数据的字节数;

4)flags:标志位,一般置0。

(4)关闭网络连接。

声明:

int close(int sockfd); 
//   成功则返回 0,否则返回-1。 
//   功能:关闭套接口 其中参数 sockfd 是关闭的套接口描述字。 
//   当对一个套接口调用 close()时, 关闭该套接口描述字,并停止连接。

二、TCP使用Socket创建服务端

(1)创建一个socket,用函数socket();

(2)绑定IP地址、端口等信息到socket上,用函数bind();

声明:

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

参数说明:

1)sockfd:套接字描述符;

2)my_addr:指向 sockaddr 结构体的指针(该结构体中保存有端口和 IP 地址 信息);

3)addlen:结构体 sockaddr 的长度。

返回类型:0(成功),-1(失败 )。

(3)开启监听,用函数listen();

声明:

int listen(int sockfd,int backlog);

参数说明:

1)sockfd:套接字描述符;

2)backlog:规定内核为此套接口排队的最大选择个数。

(4)接收客户端上来的连接,用函数accept();

声明:

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

参数说明:

1)sockfd:监听的套接字描述符;

2)cliaddr:指向结构体sockaddr 的指针;

3)addrlen:cliaddr参数指向的内存空间的长度。

(5)收发数据,用函数send()和recv(),或者read()和write();

(6)关闭网络连接;

(7)关闭监听。

三、代码实现

(1)客户端(Client)

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>

#define MYPORT  7000
#define BUFFER_SIZE 1024

int main()
{
    ///定义sockfd
    int sock_cli = socket(AF_INET,SOCK_STREAM, 0);

    ///定义sockaddr_in
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(MYPORT);  //服务器端口
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //服务器ip,inet_addr用于IPv4的IP转换(十进制转换为二进制)
    //127.0.0.1是本地预留地址
    //连接服务器,成功返回0,错误返回-1
    if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        perror("connect");
        exit(1);
    }

    char sendbuf[BUFFER_SIZE];
    char recvbuf[BUFFER_SIZE];

    while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    {/*每次读取一行,读取的数据保存在buf指向的字符数组中,成功,则返回第一个参数buf;*/
        send(sock_cli, sendbuf, strlen(sendbuf),0); ///发送
        if(strcmp(sendbuf,"exitn")==0)
            break;
        recv(sock_cli, recvbuf, sizeof(recvbuf),0); ///接收
        fputs(recvbuf, stdout);

        memset(sendbuf, 0, sizeof(sendbuf));//接受或者发送完毕后把数组中的数据全部清空(置0)
        memset(recvbuf, 0, sizeof(recvbuf));
    }
    close(sock_cli);
    return 0;
}

(2)服务端(Server)

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <thread>
#include <iostream>
#define PORT 7000
#define QUEUE 20//连接请求队列
int conn;
void thread_task()
{
}

int main()
{
   //printf("%dn",AF_INET);//IPv4协议
    printf("%dn",SOCK_STREAM);//字节流套接字
    int ss = socket(AF_INET, SOCK_STREAM, 0);//若成功则返回一个sockfd(套接字描述符)
    //printf("%dn",ss);
    struct sockaddr_in server_sockaddr;//一般是储存地址和端口的。用于信息的显示及存储使用
    /*设置 sockaddr_in 结构体中相关参数*/
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(PORT);//将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian) 
    //printf("%dn",INADDR_ANY);
    //INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。
    //一般来说,在各个系统中均定义成为0值。
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将主机的无符号长整形数转换成网络字节顺序。 
    if(bind(ss, (struct sockaddr* ) &server_sockaddr, sizeof(server_sockaddr))==-1)
    {
        perror("bind");
        exit(1);
    }
    if(listen(ss, QUEUE) == -1)
    {
        perror("listen");
        exit(1);
    }

    struct sockaddr_in client_addr;
    socklen_t length = sizeof(client_addr);
    ///成功返回非负描述字,出错返回-1
    conn = accept(ss, (struct sockaddr*)&client_addr, &length);
    //如果accpet成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。
    //accpet之后就会用新的套接字conn
    if( conn < 0 )
    {
        perror("connect");
        exit(1);
    }

    char buffer[1024];
    //创建另外一个线程
    //std::thread t(thread_task);
    //t.join();
    //char buf[1024];
    //主线程
    while(1)
    {
        memset(buffer, 0 ,sizeof(buffer));
        int len = recv(conn, buffer, sizeof(buffer), 0);//从TCP连接的另一端接收数据。

        if(strcmp(buffer, "exitn") == 0)//如果没有收到TCP另一端发来的数据则跳出循环不输出
        {
            break;
        }
        printf("%s", buffer);//如果有收到数据则输出数据
        //必须要有返回数据, 这样才算一个完整的请求
        send(conn, buffer, len , 0);//向TCP连接的另一端发送数据。
    }
    close(conn);//因为accpet函数连接成功后还会生成一个新的套接字描述符,结束后也需要关闭
    close(ss);//关闭socket套接字描述符
    return 0;
}

原文链接: https://www.cnblogs.com/JCpeng/p/14998475.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/212087

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 上午1:08
下一篇 2023年2月13日 上午1:08

相关推荐