【Linux网络编程】Socket-UDP实例

目录

知识汇总:

1.IP地址与端口号

2.主机序列与网络序列

3.多网卡/多IP

接口函数:

socket:

bind:

recvfrom:

sendto:

inet_addr:

inet_ntoa:

htons:

popen:

fgets:文件I/O与标准I/O

文件I/O与标准I/O部分:

代码:

udp_client.cc

udp_server.cc

udp_server.hpp

RingQueue.hpp

lockGuard.hpp

Thread.hpp

makefile


这份代码利用下面所有知识编写了一个简易聊天室(基于Linux操作系统)。虽然字数挺多其实并不复杂,这里如果能够看完或许会对你的知识进行一下串联,这篇文章比较杂并且网络编程这块知识需要用到系统编程的知识,希望能帮助到您。

知识汇总:

1.IP地址与端口号

我们知道同一台主机的进程间通信有system V共享内存,消息队列,信号量这些方式,而跨主机的进程间通信怎么搞呢?使用IP地址与端口号!

IP地址用来网络中标识唯一一台主机,是一个32位无符号整数,常常用192.163.1.1这样点分十进制的字符串形式表示。

端口号用来表示一台主机中的一个进程,它是一个16位无符号整数,所以端口号最小是0,最大是65536。那么端口号如何表示一个进程呢?如下图​​​​,端口号作为数组的下标,数组中存放的是进程PID。它相当于一个哈希表,根据下标即端口号就可以找到对应的进程。

这里有一个问题,为什么不直接用进程PID呢,非要多走一步端口号,感觉有点多此一举。

我是这样理解的,我们使用的应用程序都是有对应的服务器维护的,我们作为一个客户端需要和服务器进行数据交互,那么就必须明白两个问题,一是服务器在哪,二是与服务器的哪个进程进行通信。当我们通信之前,就必须知道服务器的IP地址与进程PID,那么我们怎么知道呢?IP地址我们可以视为客户端提前知晓且并不变更,那进程PID呢?服务器每重新打开一次进程,PID会一样吗?显然不会,那么我怎么找到服务器的对应进程呢?这里就陷入了一个死循环。

网络:请问您是要和服务器123.123.123.123通信吗?

客户端:对的。

网络:请告诉我你是要和服务器的哪个进程通信呢?

客户端:不知道啊?它的进程每次重新启动,进程号都会变更。

网络:对不起先生,没有进程号我们没法帮您通信。

客户端:我不跟服务器通信我怎么知道服务器的进程号。

当然只有ip地址也是可以接收到数据的,但是交由哪个进程处理,这些数据是什么意思用来干什么的,就成了问题。

为了避免这个问题,就有了端口号的概念。服务器的相应进程会放到一个固定的端口号上,客户端都是提前知晓这个端口号的,所以在通信时,客户端只需要端口号就可以找到对应进程。这也使得许多端口号约定成俗,比如常见的8080端口。

2.主机序列与网络序列

每台计算机的存储顺序不同,分为大端存储和小端存储。大端存储就是低字节放到高地址,小端存储就是高字节放到低地址。如下图,定义一个int num=1;

可以看到01放到高地址处的是大端存储,放到低地址处的是小端存储。 

既然有这种主机存储顺序的不同,那么在进行网络通信时如果两个终端存储顺序不同,那么数据就会被错误解读。为了解决这个问题,就定义了一个共同的标准,在传输网络数据的时候都以大端存储为标准。

 因为客户端发送数据,携带的目的ip与目的端口都是网络序列的,服务器端要对比数据是给哪个端口,所以本地ip和端口必须转为网络序列。

3.多网卡/多IP

这块是关于创建套接字后,使用bind函数绑定端口号与ip的一个细节。

云服务器,或者一款服务器不要bind一个具体的ip,因为服务器可能有多个网卡多个ip地址,这些ip都有可能接收指定端口的数据,所以需要在服务器启动的时候bind任意一个ip地址,这就要求在对sockaddr里面的sin_addr里面的s_addr初始化时,使用INADDR_ANY进行初始化。

接口函数:

socket:

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

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

socket函数用来创建一个套接字。domain选择协议家族来进行通信,ipv4网络通信使用AF_INET,ipv6使用AF_INET6。type是用来选择套接字类型的,  SOCK_STREAM就是面向连接,可靠的,SOCK_DGRAM就是无连接,不可靠的。protocol用0即可,选择默认合适的协议。socket创建成功会返回一个文件描述符,创建失败返回-1。 

bind:

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

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

bind函数用来绑定本地主机ip与端口号。sockfd就是创建套接字成功返回的文件描述符。

我们可以看到sockaddr是个结构体,那这个结构体的成员有哪些呢?

sockaddr结构体:

__SOCKADDR_COMMON (sa_)就是#define  __SOCKADDR_COMMON(sa_prefix) \

  sa_family_t sa_prefix##family,其实绕来绕去就是sa_family_t  sa_family,一个16位短整型变量(下面sockaddr_in结构体的第一个成员也大体一样,sa_family_t  sin_family,一个16位短整型变量),用来表示地址类型如AF_INET。char sa_data[14]就是14字节的地址数据。

不过我们在进行网络通信时,使用的是sockaddr_in类型的结构体

sockaddr_in结构体:

由上图可以看出sockaddr结构体里面的sin_port是一个16位无符号整数,in_addr结构体里面有唯一一个成员---32位无符号整数。他们分别代表一个端口号和IP地址。sin_zero结构体就是填充字段,可以看到用sockaddr结构体大小减去了sockaddr_in结构体里面的三个成员的大小,最后自然sockaddr和sockaddr_in结构体的大小就一样了。这不明摆着是让sockaddr和sockaddr_in适配么。使用时直接取地址然后强转就可以了。

所以得出下面的结论:

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结构体指针做为参数;

addrlen就是一个无符号整形,指明sockaddr结构体大小的。

recvfrom:

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

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

利用创建的套接字把接收到的最大为len字节长度的数据放到buf中,flags标志位表示是否阻塞接收(设为0即可),src_addr指针和addrlen指针分别指向一个输入性参数,用来接收发送方的IP地址端口号以及结构体大小。数据成功则返回实际接收到的字符数,失败返回-1。

sendto:

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


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

利用创建的套接字发送最大为len字节长度的数据,flags标志位表示是否阻塞发送(设为0即可),dest_addr指针指向一个sockaddr_in结构体(里面有目的ip和目的端口号),addrlen为该结构体大小。成功则返回实际传送出去的字符数,失败返回-1。

inet_addr:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

in_addr_t inet_addr(const char *cp);

inet_addr() 函数将互联网主机地址 cp 字符串从 IPv4 数字和点表示法转换为按网络字节顺序的二进制数据。 

inet_ntoa:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

     
char *inet_ntoa(struct in_addr in);

inet_ntoa() 函数将按网络字节顺序给出的互联网主机地址转换为 IPv4 点分十进制表示法的字符串。 字符串以静态分配的缓冲区,后续调用将覆盖该缓冲区。不过这里的in_addr是sockaddr_in结构体里面的一个结构体成员,这个in_addr结构体里面存放的是一个32位无符号整数(IP地址)。

htons:

#include <arpa/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);

已知端口号是16位无符号整数,ip地址是32位无符号整数。所以这里四个函数就是把主机字节序转换成网络字节序or网络字节序转换成主机字节序,IP地址用uint32_t,端口号用uint16_t。

popen:

 #include <stdio.h>

       FILE *popen(const char *command, const char *type);

       int pclose(FILE *stream);

command: 是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用 -c标志,shell 将执行这个命令。
type: 只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。 

如果调用成功,则返回一个读或者打开文件的指针,如果失败,返回NULL。

man手册中关于popen函数的解释:popen() 函数通过创建管道、分叉和调用 shell 来打开进程。 由于管道根据定义是单向的,因此类型参数可以指定只有阅读或写作,而不是两者兼而有之;生成的流相应地是只读或只写的。

popen() 的返回值在所有方面都是正常的标准 I/O 流,除了它必须使用 pclose() 而不是 fclose(3) 关闭。 写入这样的流写入命令的标准输入;该命令的标准输出与调用 popen() 的进程的标准输出相同,除非命令对此进行了更改本身。相反,从“打开的”流中读取会读取命令的标准输出,并且命令的标准输入与进程的标准输入相同称为 popen()。

fopen函数:

可以看到popen函数与fopen函数极其相似,都是标准I/O库函数,且返回值都是一个文件流指针(FILE*),都需要用close函数关闭。但是fopen函数是用于打开一个文件,而popen函数作用是创建管道并创建子进程,并利用子进程处理command命令,处理结果返回到一个文件。调用popen函数的进程就是父进程。

fgets:文件I/O与标准I/O

 #include <stdio.h>


       char *fgets(char *s, int size, FILE *stream);

fgets() 从流中最多读取一个小于size大小的字符,并将它们存储到 S 指向的缓冲区中。 读取在 EOF 或换行符后停止。 如果是新的行被读取,它被存储到缓冲区中。 终止空字节 ('0') 存储在缓冲区中最后一个字符之后。

s 代表要保存到的内存空间的首地址,可以是字符数组名,也可以是指向字符数组的字符指针变量名。size 代表的是读取字符串的长度。stream 表示从何种流中读取,可以是标准输入流 stdin,也可以是文件流,即从某个文件中读取。

可以看到fgets函数与gets函数相似,但fgets函数更为安全,并且可以从文件中读取字符,而gets()只能从标准输入中获取。fegts还能检查预留存储区的大小,保证字符串不会超出预留空间。gets() 将一行从 stdin 读取到 s 指向的缓冲区中,直到终止换行符或 EOF,它用空字节 ('0') 替换它,但并不检查缓冲区是否溢出。

文件I/O与标准I/O部分:

写到这里有一个小问题,为什么stdin可以传入FILE*类型参数,stdin是什么?明白的可以自动跳过这里 。

下面这个链接主要是文件I/O与标准I/O的知识。。。。

文件I/O与标准I/O



 

代码:

简介:下面的代码包括一个封装好的环形队列作为服务器接受客户端发送消息的容器、只需要传入互斥量指针就自动加锁自动解锁的类、封装好的线程类以及客户端服务器主程序。其实代码逻辑很简单,从udp_server.hpp的UdpServer类里面的私有成员变量入手就好。

服务器启动需要绑定一个端口号,端口号以命令行参数形式传入。

客户端启动需要在命令行输入服务器ip与端口号

udp_client.cc

#include <iostream>
using namespace std;
#include <sys/types.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <pthread.h>

//sockaddr_in结构体的头文件,当然也包含一些主机转网络序列的函数比如htons
#include <netinet/in.h>
#include <arpa/inet.h>

#include "error.hpp"




static void* rfo(void *args)
{
    int sock=*(static_cast<int*>(args));
    while(true)
    {
          //收
        char buffer[4096];
        struct sockaddr_in tmp;//输入型参数;
        socklen_t len=sizeof(tmp);//要初始化,不然没法修改;
        
        //阻塞式接收
        int n=recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&tmp,&len);
      
        if(n>0)//接收服务器数据成功
        {
            buffer[n]=0;
            cout<<buffer<<endl;
        }
    }


}

//当传入程序参数个数不对时,调用这个Usage函数告诉他什么是他妈的惊喜!
static void Usage(string proc)
{
    cout<<"Usage:\n\t"<<proc<<" serverip "<<" serverport\n"<<endl;
}







// ./udp_client serverip serverport
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    //保留输入的服务器的IP地址与端口号
    string serverip=argv[1];
    uint16_t serverport=atoi(argv[2]);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        cerr << " create socket error " << strerror(errno) << endl;
        exit(SOCKET_ERR);
    }

    //client要不要bind呢?要的!socket通信的本质[clientip,clientport ::serverip,serverport]
    //要不要自己bind呢?不需要自己bind,也不要自己bind,OS自动bind--  客户端的端口号要操作系统随机分配,防止客户端出现启动冲突。
    
    //创建线程去接收;
    pthread_t tid;
    pthread_create(&tid,nullptr,rfo,(void*)&sock);


    //明确server是谁
    struct sockaddr_in server;
    memset((void*)&server,0,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=htons(serverport);//主机序列转网络序列
    server.sin_addr.s_addr=inet_addr(serverip.c_str());//点分十进制字符串ip转成32位无符号整数并转为网络序列,这个函数有两个功能;


    while(true)
    {
       string message;
       cout<<"please Enter# ";
       getline(cin,message);


        //在首次调用sendto函数时,操作系统自动给本程序绑定IP地址和端口号,客户端不能自己绑定端口号和ip地址,因为端口号和IP地址会变。
        sendto(sock,message.c_str(),message.size(),0,(const struct sockaddr*)&server,sizeof(server));
    }


    return 0;
}

udp_server.cc


#include <iostream>
#include "udp_server.hpp"

#include <memory>
#include <cstdio>

using namespace ns_server;

// 上层的业务处理,不关心网络发送,只负责信息处理即可
// 客户端输入命令,服务器执行命令,结果返回给客户端;

// 业务1(字符串全部转大写)
string transaction(string request)
{

    string result;
    char c;

    for (auto &e : request)
    {
        if (islower(e))
        {
            c = toupper(e);
            result.push_back(c);
        }
        else
        {
            result.push_back(e);
        }
    }

    return result;
}

bool notsecure(string &command)
{
    bool ret = false;

    int pos;

    pos = command.find("rm");
    if (pos != string::npos)
        ret = true;

    pos = command.find("while");
    if (pos != string::npos)
        ret = true;

    pos = command.find("mv");
    if (pos != string::npos)
        ret = true;

    pos = command.find("kill");
    if (pos != string::npos)
        ret = true;

    return ret;
}
// 业务二(服务器端获取命令字符串,服务器执行完成后给客户端返回结果)
string excuteCommand(string command)
{
    // 1.安全检查
    if (notsecure(command))
        return "Sorry,you can do that!";

    // 2.业务逻辑处理
    FILE *fp = popen(command.c_str(), "r");//popen函数是创建管道在创建子进程,利用子进程来处理命令,并把结果输出到一个文件的,返回值是文件指针。
    if (fp == nullptr)
        return "None";

    // 3.获取结果
    char line[1024];
    string result;

    // 这里用while的原因是fgets函数遇到换行符或EOF读取结束,也就是说一次读一行,使用while循环读到文件结尾;
    while (fgets(line, sizeof(line), fp)!=nullptr)
    {
        result += line;
    }

    pclose(fp);
    return result;
}

// 当传入程序参数个数不对时,调用这个Usage函数告诉他什么是他妈的惊喜!
static void Usage(string proc)
{
    cout << "Usage:\n\t" << proc << " port\n"
         << endl;
}

// ./udp_server     serverport(服务器自己设置端口号)
int main(int argc, char *argv[])
{
    if (argc != 2) // 命令行传入参数不够
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }
    // 把字符串port转换成16位整数
    uint16_t port = atoi(argv[1]);

    // 智能指针构造UdpServer对象,构造函数需要传入自己想定义的port
    //unique_ptr<UdpServer> usvr(new UdpServer(excuteCommand, port));

    unique_ptr<UdpServer> usvr(new UdpServer(port));
    //usvr->InitServer();  // 服务器初始化
    usvr->StartServer(); // 服务器开始服务

    return 0;
}

udp_server.hpp

#pragma once
#include <iostream>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <string>
#include <pthread.h>
#include <stdlib.h>
#include <functional>
#include <unordered_map>
// sockaddr_in结构体的头文件
#include <netinet/in.h>
#include <arpa/inet.h>
#include "error.hpp"
using namespace std;
#include "RingQueue.hpp"
#include "Thread.hpp"
#include "lockGuard.hpp"

namespace ns_server
{
    // const uint16_t default_port = 8081;

    using func_t = function<string(string)>; // func_t是指代返回值为string,参数为string的函数指针;
    class UdpServer
    {

    public:
        // // 构造服务器对象必须绑定端口号,指定服务器处理方法
        // UdpServer(func_t cb, uint16_t port = default_port)
        //     : _port(port), _service(cb)

        // {

        //     cout << " Server Port : " << _port << endl;
        // }

        // 构造服务器对象必须绑定端口号,指定服务器处理方法
        UdpServer(uint16_t port)
            : _port(port), _p()

        {
            cout << " Server Port : " << _port << endl;

            pthread_mutex_init(&_mutex, nullptr); // 初始化锁;
            // 这里使用c++11 bind函数,相当于函数适配器,构建了一个可调用对象,函数参数顺序也可以占位符标定,_1,_2类似这样;
            _p = new Thread(1, bind(&UdpServer::Recv, this));
            _c = new Thread(1, bind(&UdpServer::Broadcast, this));
        }

        void StartServer()
        {

            // 1.创建socket接口,打开网络文件;
            _socket = socket(AF_INET, SOCK_DGRAM, 0);
            if (_socket < 0)
            {
                cerr << " create socket error: " << strerror(errno) << endl;
                exit(SOCKET_ERR);
            }

            cout << " create socket success: " << _socket << endl; // 3

            // 2.给服务器绑定本地IP和端口号(要知道是哪个IP哪个端口号接收数据)
            struct sockaddr_in local;
            bzero(&local, sizeof(local)); // 清零

            local.sin_family = AF_INET;
            local.sin_port = htons(_port);             // 端口号
            local.sin_addr.s_addr = htonl(INADDR_ANY); // IP地址

            if (bind(_socket, (const struct sockaddr *)&local, sizeof(local)) < 0) // 绑定本地Ip与端口号
            {
                cerr << " bind socket error: " << strerror(errno) << endl;
                exit(BIND_ERR);
            }
            cout << " bind socket success: " << _socket << endl;

            _p->run();
            _c->run();
        }

        void addUser(const string &name, const struct sockaddr_in &peer)
        {
            lockGuard lock(&_mutex);

            auto it = _onlineUser.find(name);
            if (it != _onlineUser.end())
                return;

            // 没有就插入
            _onlineUser.insert(pair<string, struct sockaddr_in>(name, peer));
        }

        // 接收client数据并记录用户ip和端口
        void Recv()
        {
            char buffer[1024];
            while (true)
            {
                // 收
                struct sockaddr_in peer; // 输入性参数,获得客户端ip与端口号
                socklen_t len = sizeof(peer);
                int n = recvfrom(_socket, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len); // 接收客户端发送过来的消息和客户端ip与端口

                if (n > 0)
                    buffer[n] = '\0';
                else
                    continue;

                // 提取client信息---debug;
                string client_ip = inet_ntoa(peer.sin_addr);
                uint16_t client_port = ntohs(peer.sin_port);                        // 网络序列转为主机序列
                cout << client_ip << "-" << client_port << " # " << buffer << endl; // 显示客户端发来的数据

                // 利用ip和端口构建一个用户名
                string name = client_ip;
                name += "-";
                name += to_string(client_port);

                
                addUser(name, peer); // 存入用户ip和端口,后面把消息转发给所有用户
                
                string message=name;
                message+=">>";
                message+=buffer;
                _rq.push(message);    // 接收到的消息加工一下存入环形队列;

                // 业务处理
                // string message = _service(buffer);
                // 发
                // sendto(_socket, message.c_str(), message.size(), 0, (struct sockaddr *)&peer, sizeof(peer));
            }
        }

        // 广播
        void Broadcast()
        {
            while (true)
            {
                string message;

                // 因为封装好的环形队列里面有信号量和互斥量,所以这里不必担心线程安全问题;
                _rq.pop(&message);

                vector<struct sockaddr_in> v;
                // 这里需要设置互斥量,因为两个线程访问了临界资源,结果具有不确定性;
                {
                    lockGuard lock(&_mutex);
                    for (auto &user : _onlineUser)
                    {
                        v.push_back(user.second);
                    }
                }

                for (auto &e : v) // 给所有用户发消息;
                {
                    sendto(_socket, message.c_str(), message.size(), 0, (const struct sockaddr *)&e, sizeof(e));
                    // 测试消息发送出去了没
                    cout << "send done..." << message << endl;
                }
            }
        }

        ~UdpServer()
        {
            pthread_mutex_destroy(&_mutex);

            // 等待线程结束
            _p->join();
            _c->join();

            // 回收堆空间;
            delete _p;
            delete _c;
        }

    private:
        int _socket;
        uint16_t _port;
        // func_t _service; // 上一个版本只是简单的IO,现在要进行业务处理;
        unordered_map<string, struct sockaddr_in> _onlineUser; // 把所有用户ip和端口保存起来,后面要给所有人转发消息;
        RingQueue<string> _rq;                                 // 环形队列存放用户发的消息;
        pthread_mutex_t _mutex;

        // 两个线程,一个收,一个发;
        Thread *_p;
        Thread *_c;
    };
}
#pragma once
       enum
        {   
            USAGE_ERR=1,
            SOCKET_ERR,
            BIND_ERR

        };

RingQueue.hpp

#pragma once

#include<iostream>
#include<semaphore.h>

#include<ctime>
#include<unistd.h>
#include<vector>



using namespace std;

const int N=50;

template<class T>
class RingQueue
{

    void P(sem_t* sem)
    {
        sem_wait(sem);
    }

    void V(sem_t* sem)
    {
        sem_post(sem);
    }

    void Lock(pthread_mutex_t& mutex)
    {
        pthread_mutex_lock(&mutex);
    }
     void UnLock(pthread_mutex_t& mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(int num=N):_ring(num),_cup(num),_consumer_step(0),_productor_step(0)
    {
       sem_init(&_data_sem,0,0);
       sem_init(&_space_sem,0,_cup);
       pthread_mutex_init(&_c_mutex,nullptr);
       pthread_mutex_init(&_p_mutex,nullptr);

    }

    void push(const T& in)
    {
        P(&_space_sem);
        Lock(_p_mutex);
        _ring[_productor_step++]=in;
        _productor_step %= _cup;
        //消费者信号量加一;(数据)
        V(&_data_sem);
        UnLock(_p_mutex);
    }

    void pop(T* out)
    {
        P(&_data_sem);
        Lock(_c_mutex);
        *out=_ring[_consumer_step++];
        _consumer_step %= _cup;
        //生产者信号量加一(空间)
        V(&_space_sem);
        UnLock(_c_mutex);
    }

    ~RingQueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_space_sem);

        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
    }

private:

    vector<T> _ring;//数组模拟环形队列
    int  _cup;//容量

    sem_t _data_sem;//消费者信号量
    sem_t _space_sem;//生产者信号量

    int _consumer_step;//消费者下标
    int _productor_step;//生产者下标


    // 单生产和单消费不存在竞争问题,只要有信号量即可;但是多生产和多消费的线程,可能都申请到了信号量,但是都在竞争同一块资源,无法保证原子性;
    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;

};

lockGuard.hpp

#pragma once

#include <pthread.h>
#include <iostream>

class Mutex//成员:加锁函数和解锁函数
{
public:
    Mutex(pthread_mutex_t* pmutex):_pmutex(pmutex)   {}
    
    void lock()
    {
        pthread_mutex_lock(_pmutex);
    }
   
       void unlock()
    {
        pthread_mutex_unlock(_pmutex);
    }
   

    ~Mutex(){}

private:
    pthread_mutex_t* _pmutex;//需要传入一个互斥量(锁)的指针;
};


//对Mutex进行二次封装;
//创建该对象时自动加锁,析构时自动解锁;
class lockGuard
{   
public:
    lockGuard(pthread_mutex_t* pmutex):_mutex(pmutex)//利用锁的指针构建Mutex对象
    {
        _mutex.lock();
    }

   ~lockGuard()
    {
        _mutex.unlock();
    }

private:
    Mutex _mutex;//类内创建对象
};

Thread.hpp

#pragma once

#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <cstdlib>
#include <string>



class Thread
{
public:
    //typedef void (*func_t) (void*);
    using func_t=function<void()>;//fun_t:无返回值,无参数的函数指针;
    typedef enum
    {
        NEW=0,
        RUNNING,
        EXITED
    }ThreadStatus;



public:

   
        Thread(int num,func_t func)
        :_tid(0),
        _status(NEW),
        _func(func)
       
        {
            char name[128];
            snprintf(name,sizeof(name),"thread-%d",num);
            _name=name;
        }

        //状态:new,running,exited
        int status()
        {
            return _status;
        }
        //线程名
        std::string threadname()
        {
            return _name;
        }
        //线程ID(共享库中的进程地址空间的虚拟地址)
        pthread_t threadid()
        {
            if(_status==RUNNING)//线程已经被创建,线程id已经输入到成员变量_tid中;
                return _tid;
            else 
            {
                std::cout<<"thread is not running,no tid!"<<std::endl;
                return 0;
            }
        }

        static void* runHelper(void *args)
        {
            //静态成员函数不能访问类内所有成员,因为没有this指针;
            Thread* td=(Thread*)args;
            (*td)();//该对象调用仿函数;
            return nullptr; 
        }

        void operator()()//仿函数
        {
            _func();
        }

        //创建线程
        void run()
        {
            //因为runHelper函数必须只能有一个void*参数,所以runHelper函数在类内必须定义为static,这样才没有this指针;
            int n=pthread_create(&_tid,nullptr,runHelper,this);
            if(n!=0) return exit(0);//线程创建失败,那么直接退出进程;
            _status=RUNNING;
        }

        //等待线程结束
        void join()
        {
            int n=pthread_join(_tid,nullptr);
            if(n!=0) 
            {
                std::cerr<<"main thread join thread "<<_name<<" error "<<std::endl;
                return;
            }
            _status=EXITED;//线程退出;
        }
private:
    pthread_t _tid;//线程ID(原生线程库中为该线程所创建的TCB起始虚拟地址)
    std::string _name;//线程名
    func_t _func;//线程要执行的回调
    //void* _args;//线程回调函数参数
    ThreadStatus _status;//枚举类型:状态

};

makefile

.PHONY:all
all:udp_server udp_client

udp_server:udp_server.cc
	g++ $^ -o $@ -std=c++11 -lpthread
udp_client:udp_client.cc
	g++ $^ -o $@ -std=c++11 -lpthread

.PHONY:clean
clean:
	rm -f udp_client udp_server

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一章 概论 .................................................................................................................. 1 1.1 网络的历史................................................................. ...................................... 1 1.2 OSI 模型........................................................................................................... 3 1.3 Internet 体系模型.............................................................................................. 4 1.4 客户/服务器模型............................................................................................... 5 1.4 UNIX 的历史 ................................................................................................... 7 1.4.1 Unix 诞生前的故事 ................................................................................. 7 1.4.2 UNIX 的诞生.......................................................................................... 8 1.4.3 1979 – UNIX 第七版 ............................................................................. 10 1.4.4 UNIX 仅仅是历史吗?............................................................................. 11 1.5 Linux 的发展.................................................................................................. 11 1.5.1 Linux 的发展历史 .................................................................................. 12 1.5.2 什么叫 GNU? ...................................................................................... 12 1.5.3 Linux 的特色 ........................................................................................ 13 1.5.4 硬件需求............................................................................................... 14 1.5.5 Linux 可用的软件 ................................................................................. 14 1.5.6 为什么选择 Linux ? ............................................................................ 15 1.6 Linux 和 Unix 的发展 .................................................................................... 15 第二章 UNIX/Linux 模型...............................................................................................17 2.1 UNIX/Linux 基本结构.......................................................................................17 2.2 输入和输出......................................................................................................19 2.2.1 UNIX/Linux 文件系统简介 ......................................................................19 2.2.2 流和标准 I/O 库......................................................................................20 2.3 进程 ................................................................................................................21 第三章 进程控制 ..........................................................................................................22 3.1 进程的建立与运行 ...........................................................................................22 3.1.1 进程的概念 ............................................................................................22 3.1.2 进程的建立 ............................................................................................22 3.1.3 进程的运行 ............................................................................................24 3.1.4 数据和文件描述符的继承 .......................................................................29 3.2 进程的控制操作...............................................................................................31 3.2.1 进程的终止 ............................................................................................31 3.2.2 进程的同步 ............................................................................................32 3.2.3 进程终止的特殊情况 ..............................................................................33 3.2.4 进程控制的实例 .....................................................................................33 3.3 进程的属性......................................................................................................38 3.3.1 进程标识符 ............................................................................................38 3.3.2 进程的组标识符 .....................................................................................39 3.3.3 进程环境................................................................................................40 3.3.4 进程的当前目录 .....................................................................................42 3.3.5 进程的有效标识符..................................................................................43 3.3.6 进程的资源 ............................................................................................44 3.3.7 进程的优先级.........................................................................................45 3.4 守护进程 .........................................................................................................46 3.4.1 简介.......................................................................................................46 3.4.2 守护进程的启动 ............................................................................................46 3.4.3 守护进程的错误输出 ..............................................................................46 3.4.4 守护进程的建立 .....................................................................................48 3.5 本章小结 .........................................................................................................49 第四章 进程间通信.......................................................................................................50 4.1 进程间通信的一些基本概念 .............................................................................50 4.2 信号 ................................................................................................................50 4.2.1 信号的处理 ............................................................................................52 4.2.2 信号与系统调用的关系...........................................................................54 4.2.3 信号的复位 ............................................................................................55 4.2.4 在进程间发送信号..................................................................................56 4.2.5 系统调用 alarm()和 pause()......................................................................58 4.2.6 系统调用 setjmp()和 longjmp().................................................................62 4.3 管道 ................................................................................................................63 4.3.1 用 C 来建立、使用管道 ..........................................................................65 4.3.2 需要注意的问题 .....................................................................................72 4.4 有名管道 .........................................................................................................72 4.4.1 有名管道的创建 .....................................................................................72 4.4.2 有名管道的 I/O 使用...............................................................................73 4.4.3 未提到的关于有名管道的一些注意 .........................................................75 4.5 文件和记录锁定...............................................................................................75 4.5.1 实例程序及其说明..................................................................................75 4.5.2 锁定中的几个概念..................................................................................78 4.5.3 System V 的咨询锁定..............................................................................78 4.5.4 BSD 的咨询式锁定 .................................................................................79 4.5.5 前面两种锁定方式的比较 .......................................................................81 4.5.6 Linux 的其它上锁技术 ............................................................................81 4.6 System V IPC ...................................................................................................84 4.6.1 ipcs 命令 ................................................................................................85 4.6.2 ipcrm 命令..............................................................................................86 4.7 消息队列(Message Queues)...........................................................................86 4.7.1 有关的数据结构 .....................................................................................86 4.7.2 有关的函数 ............................................................................................89 4.7.3 消息队列实例——msgtool,一个交互式的消息队列使用工具 ..................94 4.8 信号量(Semaphores) .........................................................................................97 4.8.1 有关的数据结构 .....................................................................................98 4.8.2 有关的函数 ............................................................................................99 4.8.3 信号量的实例——semtool,交互式的信号量使用工具........................... 103 4.9 共享内存(Shared Memory) .............................................................................. 109 4.9.1 有关的数据结构 ................................................................................... 109 4.9.2 有关的函数 .......................................................................................... 110 4.9.3 共享内存应用举例——shmtool,交互式的共享内存使用工具................... 112 4.9.4 共享内存与信号量的结合使用 .............................................................. 114 第五章 通信协议简介 ................................................................................................. 120 5.1 引言 .............................................................................................................. 120 5.2 XNS(Xerox Network Systems)概述.............................................................. 120 5.2.1 XNS 分层结构...................................................................................... 120 IPX/SPX 协议概述........................................................................................ 122 5.3 5.3.1 网际包交换(IPX) ............................................................................. 122 5.3.2 排序包交换(SPX)............................................................................. 124 5.4 Net BIOS 概述................................................................................................ 124 5.5 Apple Talk 概述 .............................................................................................. 125 5.6 TCP/IP 概述................................................................................................... 126 5.6.1 TCP/IP 结构模型 .................................................................................. 126 5.6.2 Internet 协议(IP)............................................................................... 127 5.6.3 传输控制协议(TCP) ......................................................................... 132 5.6.4 用户数据报文协议................................................................................ 134 5.7 小结 .............................................................................................................. 135 第六章 Berkeley 套接字 ............................................................................................. 136 6.1 引言 ............................................................................................................. 136 6.2 概述 ............................................................................................................. 136 6.2.1 Socket 的历史...................................................................................... 136 6.2.2 Socket 的功能...................................................................................... 136 6.2.3 套接字的三种类型............................................................................... 138 6.3 Linux 支配的网络协议................................................................................... 141 6.3.1 什么是 TCP/IP? ................................................................................... 141 6.4 套接字地址................................................................................................... 142 6.4.1 什么是 Socket? .................................................................................. 142 6.4.2 Socket 描述符...................................................................................... 142 6.4.3 一个套接字是怎样在网络上传输数据的?............................................ 143 6.5 套接字的一些基本知识 ................................................................................. 144 6.5.1 基本结构............................................................................................. 144 6.5.2 基本转换函数...................................................................................... 145 6.6 基本套接字调用............................................................................................ 147 6.6.1 socket() 函数....................................................................................... 147 6.6.2 bind() 函数 ......................................................................................... 148 6.6.3 connect()函数 ...................................................................................... 150 6.6.4 listen() 函数........................................................................................ 151 6.6.5 accept()函数 ........................................................................................ 152 6.6.6 send()、recv()函数 ............................................................................... 154 6.6.7 sendto() 和 recvfrom() 函数 ................................................................. 155 6.6.8 close()和 shutdown()函数...................................................................... 156 6.6.9 setsockopt() 和 getsockopt() 函数 ......................................................... 157 6.6.10 getpeername()函数.............................................................................. 157 6.6.11 gethostname()函数.............................................................................. 158 6.7 DNS 的操作.................................................................................................. 158 6.7.1 理解 DNS............................................................................................ 158 6.7.2 和 DNS 有关的函数和结构 .................................................................. 158 6.7.3 DNS 例程............................................................................................ 159 6.8 套接字的 Client/Server 结构实现的例子.......................................................... 160 6.8.1 简单的流服务器 .................................................................................. 161 6.8.2 简单的流式套接字客户端程序 ............................................................. 163 6.8.3 数据报套接字例程(DatagramSockets)............................................... 165 6.9 保留端口 ...................................................................................................... 169 6.9.1 简介.................................................................................................... 169 6.9.2 保留端口............................................................................................. 170 6.10 五种 I/O 模式................................................................................................. 179 6.10.1 阻塞 I/O 模式 .................................................................................... 179 6.10.2 非阻塞模式 I/O.................................................................................. 180 6.10.3 I/O 多路复用 ..................................................................................... 181 6.10.4 信号驱动 I/O 模式 ............................................................................. 182 6.10.5 异步 I/O 模式 .................................................................................... 185 6.10.6 几种 I/O 模式的比较.......................................................................... 186 6.10.7 fcntl()函数 ......................................................................................... 186 6.10.8 套接字选择项 select()函数.................................................................. 187 6.11 带外数据..................................................................................................... 190 6.11.1 TCP 的带外数据 ................................................................................ 190 6.11.2 OOB 传输套接字例程(服务器代码 Server.c) ................................... 193 6.11.3 OOB 传输套接字例程(客户端代码 Client.c).................................... 196 6.11.4 编译例子 ........................................................................................... 199 6.12 使用 Inetd(Internet 超级服务器) ............................................................... 199 6.12.1 简介.................................................................................................. 199 6.12.2 一个简单的 inetd 使用的服务器程序 hello inet service.......................... 199 6.12.3 /etc/services 和 /etc/inetd.conf 文件 ..................................................... 200 6.12.4 一个复杂一些的 inetd 服务器程序 ...................................................... 201 6.12.5 一个更加复杂的 inetd 服务器程序 ...................................................... 203 6.12.6 程序必须遵守的安全性准则............................................................... 205 6.12.7 小结.................................................................................................. 205 6.13 本章总结 .................................................................................................... 205 第七章 网络安全性..................................................................................................... 206 7.1 网络安全简介 ................................................................................................ 206 7.1.1 网络安全的重要性................................................................................ 206 7.1.2 信息系统安全的脆弱性......................................................................... 207 7.2 Linux 网络不安全的因素 ................................................................................ 209 7.3 Linux 程序员安全........................................................................................... 211 7.3.1 系统子程序 .......................................................................................... 212 7.3.2 标准 C 函数库....................................................................................... 214 7.3.3 书写安全的 C 程序................................................................................ 216 7.3.4 SUID/SGID 程序指导准则...................................................................... 217 7.3.5 root 程序的设计.................................................................................... 218 7.4 小结 .............................................................................................................. 219 第八章 Ping 例程 ....................................................................................................... 220 8.1 Ping 命令简介 ................................................................................................ 220 8.2 Ping 的基本原理............................................................................................. 220 8.3 小结 .............................................................................................................. 221 第九章 tftp 例程......................................................................................................... 222 9.1 tftp 协议简介.................................................................................................. 222 9.2 tftp 的使用 ..................................................................................................... 222 9.3 tftp 的原理 ..................................................................................................... 223 9.3 tftp 的基本结构 .............................................................................................. 223 9.4 小节 .............................................................................................................. 225 第十章 远程命令执行 ................................................................................................. 226 10.1 引言 ............................................................................................................ 226 10.2 rcmd 函数和 rshd 服务器............................................................................... 227 10.3 rexec 函数和 rexecd 服务器........................................................................... 233 第十一章 远程注册..................................................................................................... 235 11.1 简介............................................................................................................. 235 11.2 终端行律和伪终端........................................................................................ 235 11.3 终端方式字和控制终端................................................................................. 239 11.4 rlogin 概述.................................................................................................... 242 11.5 窗口环境...................................................................................................... 242 11.6 流控制与伪终端方式字................................................................................. 243 11.7 rlogin 客户程序............................................................................................. 245 11.8 rlogin 服务器 ................................................................................................ 246 第十二章 远程过程调用.............................................................................................. 249 12.1 引言 ............................................................................................................ 249 12.2 远程过程调用模型 ....................................................................................... 249 12.3 传统过程调用和远程过程调用的比较 ........................................................... 250 12.4 远程过程调用的定义.................................................................................... 252 12.5 远程过程调用的有关问题............................................................................. 252 12.5.1 远程过程调用传送协议....................................................................... 253 12.5.2 Sun RPC ........................................................................................... 254 12.5.3 Xerox Courier .................................................................................... 254 12.5.4 Apollo RPC........................................................................................ 255 12.6 stub 过程简介............................................................................................... 256 12.7 rpcgen 简介 .................................................................................................. 256 12.8 分布式程序生成的例子 ................................................................................ 257 12.8.1 我们如何能够构造出一个分布式应用程序........................................... 257 12.9 小结 ............................................................................................................ 283 第十三章 远程磁带的访问 .......................................................................................... 284 13.1 简介 ............................................................................................................ 284 13.2 Linux 磁带驱动器的处理 .............................................................................. 285 13.3 rmt 协议....................................................................................................... 285 13.4 rmt 服务器设计分析 ..................................................................................... 286 第十四章 WWW 上 HTTP 协议.................................................................................. 290 14.1 引言............................................................................................................ 290 14.2 HTTP 客户请求........................................................................................... 290 14.2.1 客户端 .............................................................................................. 290 14.2.2 服务器端........................................................................................... 290 14.2.3 Web 请求简介.................................................................................... 291 14.2.4 HTTP – HyperText Transfer Protocol 超文本传输协议 ........................... 295 14.3 Web 编程 .................................................................................................... 297 14.4 小结 ........................................................................................................... 301 附录 A 有关网络通信的服务和网络库函数................................................................... 302 附录 B Vi 使用简介..................................................................................................... 319 B.1 Vi 基本观念................................................................................................... 319 B.1.1 进入与离开.......................................................................................... 319 B.1.2 Vi 输入模式 ......................................................................................... 319 B.2 Vi 基本编辑................................................................................................... 320 B.2.1 删除与修改.......................................................................................... 320 B.3 Vi 进阶应用................................................................................................... 320 B.3.1 移动光标 ............................................................................................. 320 B.3.2 进阶编辑命令 ...................................................................................... 322 B.3.3 文件命令 ............................................................................................. 322 附录 C Linux 下 C 语言使用与调试简介 ...................................................................... 324 C.1 C 语言编程 ................................................................................................... 324 C.2 什么是 C? ..................................................................................................... 324 C.3 GNU C 编译器............................................................................................... 324 C.3.1 使用 GCC ............................................................................................ 324 C.3.2 GCC 选项 ............................................................................................ 325 C.3.3 优化选项 ............................................................................................. 325 C.3.4 调试和剖析选项................................................................................... 325 C.3.5 用 gdb 调试 GCC 程序.......................................................................... 326 C.4 另外的 C 编程工具 ........................................................................................ 330 C.4.1 Xxgdb.................................................................................................. 330 C.4.2 Calls .................................................................................................... 331 C.4.3 cproto .................................................................................................. 332 C.4.4 Indent .................................................................................................. 333 C.4.5 Gprof................................................................................................... 334 C.4.6 f2c 和 p2c ............................................................................................ 335 附录 D Ping 源码 ........................................................................................................ 336 附录 E TFTP 服务器程序源码 ..................................................................................... 362
目 录 第1篇 Linux网络开发基础 第1章 Linux操作系统概述 2 1.1 Linux发展历史 2 1.1.1 Linux的诞生和发展 2 1.1.2 Linux名称的由来 1.2 Linux的发展要素 3 1.2.1 UNIX操作系统 4 1.2.2 Minix操作系统 4 1.2.3 POSIX 标准 4 1.3 Linux与UNIX的异同 5 1.4 操作系统类型选择和内核版本的选择 5 1.4.1 常见的不同公司发行的Linux异同 6 1.4.2 内核版本的选择 6 1.5 Linux的系统架构 7 1.5.1 Linux内核的主要模块 7 1.5.2 Linux的文件结构 9 1.6 GNU通用公共许可证 10 1.6.1 GPL许可证的历史 10 1.6.2 GPL的自由理念 10 1.6.3 GPL的基本条款 11 1.6.4 关于GPL许可证的争议 12 1.7 Linux软件开发的可借鉴之处 12 1.8 小结 13 第2章 Linux编程环境 14 2.1 Linux环境下的编辑器 14 2.1.1 vim使用简介 14 2.1.2 使用vim建立文件 15 2.1.3 使用vim编辑文本 16 2.1.4 vim的格式设置 18 2.1.5 vim配置文件.vimrc 19 2.1.6 使用其他编辑器 19 2.2 Linux下的GCC编译器工具集 19 2.2.1 GCC简介 19 2.2.2 编译程序的基本知识 21 2.2.3 单个文件编译成执行文件 22 2.2.4 编译生成目标文件 22 2.2.5 多文件编译 23 2.2.6 预处理 24 2.2.7 编译成汇编语言 24 2.2.8 生成和使用静态链接库 25 2.2.9 生成动态链接库 26 2.2.10 动态加载库 29 2.2.11 GCC常用选项 31 2.2.12 编译环境的搭建 33 2.3 Makefile文件简介 34 2.3.1 一个多文件的工程例子 34 2.3.2 多文件工程的编译 36 2.3.3 Makefile的规则 37 2.3.4 Makefile中使用变量 39 2.3.5 搜索路径 43 2.3.6 自动推导规则 44 2.3.7 递归make 44 2.3.8 Makefile中的函数 46 2.4 用GDB调试程序 47 2.4.1 编译可调试程序 48 2.4.2 使用GDB调试程序 49 2.4.3 GDB常用命令 52 2.4.4 其他的GDB 59 2.5 小结 60 第3章 文件系统简介 61 3.1 Linux下的文件系统 61 3.1.1 Linux下文件的内涵 61 3.1.2 文件系统的创建 62 3.1.3 挂接文件系统 64 3.1.4 索引节点inode 65 3.1.5 普通文件 66 3.1.6 设备文件 66 3.1.7 虚拟文件系统VFS 68 3.2 文件的通用操作方法 72 3.2.1 文件描述符 72 3.2.2 打开创建文件open()、create()函数 72 3.2.3 关闭文件close()函数 76 3.2.4 读取文件read()函数 77 3.2.5 写文件write()函数 79 3.2.6 文件偏移lseek()函数 80 3.2.7 获得文件状态fstat()函数 83 3.2.8 文件空间映射mmap()函数 85 3.2.9 文件属性fcntl()函数 88 3.2.10 文件输入输出控制ioctl()函数 92 3.3 socket文件类型 93 3.4 小结 93 第4章 程序、进程和线程 94 4.1 程序、进程和线程的概念 94 4.1.1 程序和进程的差别 94 4.1.2 Linux环境下的进程 95 4.1.3 进程和线程 96 4.2 进程产生的方式 96 4.2.1 进程号 96 4.2.2 进程复制fork() 97 4.2.3 system()方式 98 4.2.4 进程执行exec()函数系列 99 4.2.5 所有用户态进程的产生进程init 100 4.3 进程间通信和同步 101 4.3.1 半双工管道 101 4.3.2 命名管道 107 4.3.3 消息队列 108 4.3.4 消息队列的一个例子 114 4.3.5 信号量 116 4.3.6 共享内存 121 4.3.7 信号 124 4.4 Linux下的线程 127 4.4.1 多线程编程实例 127 4.4.2 Linux下线程创建函数pthread_create() 129 4.4.3 线程的结束函数pthread_join()和pthread_exit() 129 4.4.4 线程的属性 130 4.4.5 线程间的互斥 132 4.4.6 线程中使用信号量 133 4.5 小结 136 第2篇 Linux用户层网络编程 第5章 TCP/IP协议族简介 138 5.1 OSI网络分层介绍 138 5.1.1 OSI网络分层结构 138 5.1.2 OSI的7层网络结构 139 5.1.3 OSI参考模型中的数据传输 140 5.2 TCP/IP协议栈 141 5.2.1 TCP/IP协议栈参考模型 141 5.2.2 主机到网络层协议 143 5.2.3 IP协议 144 5.2.4 网际控制报文协议(ICMP) 146 5.2.5 传输控制协议(TCP) 150 5.2.6 用户数据报文协议(UDP) 154 5.2.7 地址解析协议(ARP) 156 5.3 IP地址分类与TCP/UDP端口 158 5.3.1 因特网中IP地址的分类 159 5.3.2 子网掩码(subnet mask address) 161 5.3.3 IP地址的配置 162 5.3.4 端口 163 5.4 主机字节序和网络字节序 163 5.4.1 字节序的含义 164 5.4.2 网络字节序的转换 164 5.5 小结 166 第6章 应用层网络服务程序简介 167 6.1 HTTP协议和服务 167 6.1.1 HTTP协议概述 167 6.1.2 HTTP协议的基本过程 168 6.2 FTP协议和服务 170 6.2.1 FTP协议概述 170 6.2.2 FTP协议的工作模式 172 6.2.3 FTP协议的传输方式 172 6.2.4 一个简单的FTP过程 173 6.2.5 常用的FTP工具 173 6.3 TELNET协议和服务 174 6.3.1 远程登录的基本概念 174 6.3.2 使用TELNET协议进行远程登录的工作过程 174 6.3.3 TELNET协议 174 6.4 NFS协议和服务 176 6.4.1 安装NFS服务器和客户端 176 6.4.2 服务器端的设定 176 6.4.3 客户端的操作 177 6.4.4 showmount命令 177 6.5 自定义网络服务 177 6.5.1 xinetd/inetd 178 6.5.2 xinetd服务配置 178 6.5.3 自定义网络服务 179 6.6 小结 180 第7章 TCP网络编程基础 181 7.1 套接字编程基础知识 181 7.1.1 套接字地址结构 181 7.1.2 用户层和内核层交互过程 183 7.2 TCP网络编程流程 184 7.2.1 TCP网络编程架构 184 7.2.2 创建网络插口函数socket() 186 7.2.3 绑定一个地址端口对bind() 189 7.2.4 监听本地端口listen 192 7.2.5 接受一个网络请求accept() 194 7.2.6 连接目标网络服务器connect() 199 7.2.7 写入数据函数write() 200 7.2.8 读取数据函数read() 201 7.2.9 关闭套接字函数close() 201 7.3 服务器/客户端的简单例子 202 7.3.1 例子功能描述 202 7.3.2 服务器网络程序 203 7.3.3 服务器读取和显示字符串 205 7.3.4 客户端的网络程序 205 7.3.5 客户端读取和显示字符串 206 7.3.6 编译运行程序 206 7.4 截取信号的例子 207 7.4.1 信号处理 207 7.4.2 信号SIGPIPE 208 7.4.3 信号SIGINT 208 7.5 小结 208 第8章 服务器和客户端信息的获取 210 8.1 字节序 210 8.1.1 大端字节序和小端字节序 210 8.1.2 字节序转换函数 212 8.1.3 一个字节序转换的例子 214 8.2 字符串IP地址和二进制IP地址的转换 217 8.2.1 inet_xxx()函数 217 8.2.2 inet_pton()和inet_ntop()函数 219 8.2.3 使用8.2.1节地址转换函数的例子 220 8.2.4 使用函数inet_pton()和函数inet_ntop()的例子 223 8.3 套接字描述符判定函数issockettype() 223 8.3.1 进行文件描述符判定的函数issockettype() 224 8.3.2 main()函数 224 8.4 IP地址与域名之间的相互转换 225 8.4.1 DNS原理 225 8.4.2 获取主机信息的函数 226 8.4.3 使用主机名获取主机信息的例子 228 8.4.4 函数gethostbyname()不可重入的例子 230 8.5 协议名称处理函数 232 8.5.1 xxxprotoxxx()函数 232 8.5.2 使用协议族函数的例子 233 8.6 小结 236 第9章 数据的IO和复用 237 9.1 IO函数 237 9.1.1 使用recv()函数接收数据 237 9.1.2 使用send()函数发送数据 239 9.1.3 使用readv()函数接收数据 240 9.1.4 使用writev()函数发送数据 240 9.1.5 使用recvmsg()函数接收数据 242 9.1.6 使用sendmsg()函数发送数据 244 9.1.7 IO函数的比较 246 9.2 使用IO函数的例子 246 9.2.1 客户端处理框架的例子 246 9.2.2 服务器端程序框架 248 9.2.3 使用recv()和send()函数 249 9.2.4 使用readv()和write()函数 251 9.2.5 使用recvmsg()和sendmsg()函数 253 9.3 IO模型 256 9.3.1 阻塞IO模型 256 9.3.2 非阻塞IO模型 257 9.3.3 IO复用 257 9.3.4 信号驱动IO模型 258 9.3.5 异步IO模型 258 9.4 select()函数和pselect()函数 259 9.4.1 select()函数 259 9.4.2 pselect()函数 261 9.5 poll()函数和ppoll()函数 262 9.5.1 poll()函数 263 9.5.2 ppoll()函数 264 9.6 非阻塞编程 264 9.6.1 非阻塞方式程序设计介绍 264 9.6.2 非阻塞程序设计的例子 264 9.7 小结 266 第10章 基于UDP协议的接收和发送 267 10.1 UDP编程框架 267 10.1.1 UDP编程框图 267
Linux网络编程中的TCP和UDP是两种常见的传输协议。 TCP(Transmission Control Protocol)是一种基于连接的可靠传输协议。它提供了面向连接、可靠的数据传输服务。在TCP通信中,数据被分割成小的数据块,通过TCP连接按序传输,并且保证数据的可靠性,即使在网络拥塞或数据丢失的情况下也能重新传输丢失的数据。TCP适用于对可靠性要求较高的应用程序,如文件传输、电子邮件和网页浏览。 UDP(User Datagram Protocol)是一种无连接的不可靠传输协议。它提供了一种无序、不可靠的数据传输服务。在UDP通信中,数据以数据包(也称为数据报)的形式发送,不进行连接建立和断开,也不保证数据的可靠性和按序传输。UDP适用于对实时性要求较高、对数据可靠性要求较低的应用程序,如音视频流媒体、在线游戏等。 在Linux中进行TCP和UDP网络编程可以使用Socket API。该API提供了一组函数和数据结构,用于创建套接字(socket)、绑定(bind)套接字到特定的IP地址和端口、监听(listen)连接请求、接受(accept)连接、建立连接(connect)、发送(send)和接收(receive)数据等操作。 你可以使用C语言或其他支持Socket API的编程语言来进行Linux网络编程,通过调用Socket API提供的函数来实现TCP或UDP通信。在编程过程中,你需要了解TCP和UDP的特点、使用套接字创建相应的连接类型、发送和接收数据的方式等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值