Linux网络编程

Linux的优点之一就是在于它丰富而稳定的网络协议栈,其范围是从协议无关层(如通用的socket层接口和设备层)到各种网络协议的实现
对于网络理论介绍一般采用OSI模型,但是Linux中网络栈的介绍一般分为四层的Internet模型,Linux下TCP/IP四层模型概念一般为网络接口层,网际层,传输层,应用层。

TCP是重要的传输层协议,目的是允许数据同网络上的其他节点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,而且完成数据的可靠传输TCP 协议具有严格的内装差错检验算法确保数据的完整性TCP 是面向字节的顺序协议,这意味着包内的每个字节被分配一个顺序编号,并分配给每包一个顺序编号
Linux中的网络编程通过Socket(套接字)接口实现,Socket是一种文件描述符
地址结构

struct sockaddr_in
{
   short int sin_family;  /* Internet地址族 */
   unsigned short int sin_port;  /* 端口号 */
   struct in_addr sin_addr;   /* IP地址 */
   unsigned char sin_zero[8];  /* 填0 */
};

IP地址通常由数字加点(192.168.0.1)的形式表示,而在struct in_addr中使用的是IP地址是由32位的整数表示
的,为了转换我们可以使用下面两个函数:

int inet_aton(const char *cp,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)

函数里面 a 代表 ascii n 代表network.第一个函数表示将a.b.c.d形式的IP转换为32位的IP,存储在 inp指针里面。第二个是将32位IP转换为a.b.c.d的格式

进行Socket编程的常用函数有:
socket
创建一个socket
bind
用于绑定IP地址和端口号到socket
connect
该函数用于绑定之后的client端与服务器建立连接
listen
设置能处理的最大连接要求,Listen()并未开始接收连线,只是设置socket为listen模式。
accept
用来接受socket连接。
send
发送数据
recv
接收数据

基于TCP/IP的服务器
搭建过程:
1. 创建一个socket,用函数socket()
2. 绑定IP地址、端口等信息到socket上,用函数bind()
3.设置允许的最大连接数,用函数listen()
4.接收客户端上来的连接,用函数accept()
5.收发数据,用函数send()和recv(),或者read()和write()
6.关闭网络连接

案例:

// 初始化套接字,返回监听套接字
int init_socket()
{
        //1、创建socket
    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_socket == -1)
    {
        perror ("socket");
        return -1;
    }

    // 2、命名套接字,绑定本地的ip地址和端口
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family  = AF_INET;     // 设置地址族
    addr.sin_port    = htons(PORT); // 设置本地端口
    addr.sin_addr.s_addr = htonl(INADDR_ANY);   // 使用本地的任意IP地址

    int  ret = bind(listen_socket,  (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror ("bind");
        return -1;
    }

    // 3、监听本地套接字
    ret = listen(listen_socket, 5);
    if (ret == -1)
    {
        perror ("listen");
        return -1;
    }

    printf ("等待客户端连接.......\n");
    return listen_socket;
}

// 处理客户端连接,返回与连接上的客户端通信的套接字
int  MyAccept(int listen_socket)
{
    // 4、接收连接
    // 监听套接字不能用来与客户端进行通信,它的职责是监听客户端的连接
    // accpet 处理客户端的连接,如果成功接收,会返回一个新的套接字,用来与客户端进行通信
    // accept的第三个参数 是一个传入传出参数
    struct sockaddr_in client_addr; // 用来保存客户端的ip和端口信息
    int len = sizeof(client_addr);
    int client_socket = accept(listen_socket,   (struct sockaddr *)&client_addr,  &len);
    if (client_socket == -1)
    {
        perror ("accept");
    }

    printf ("成功接收一个客户端: %s\n", inet_ntoa(client_addr.sin_addr));

    return client_socket;
}

// 把 负责处理客户端通信的函数改成线程的工作函数
void* hanld_client(void* v)
{
    int client_socket = (int)v;

    char buf[1024];
    int i;
    while(1)
    {
        // 从客户端读数据
        int ret = read(client_socket, buf, 1024);
        if (ret == -1)
        {
            perror ("read");
            break;
        }

        // 代表客户端退出
        if (ret == 0)
        {
            printf ("客户端退出\n");
            break;
        }
        buf[ret] = '\0';
        for (i = 0; i < ret-1; i++)
        {
            buf[i] = buf[i] + 'A' - 'a';
        }

        write(client_socket, buf, ret);
        printf ("发送数据:%s\n", buf);
    }


    close (client_socket);
}

int main()
{
    // 初始化套接字
    int listen_socket = init_socket();

    while (1)
    {
            // 获取与客户端连接的套接字
        int client_socket = MyAccept(listen_socket);

        // 创建一个线程去处理客户端的请求,主线程依然负责监听
        pthread_t id;
        pthread_create(&id, NULL, hanld_client,  (void *)client_socket);

        pthread_detach(id); // 线程分离
    }
    close (listen_socket);

    return 0;
}

通过这两个函数可以处理与客户端的连接,返回与客户端通信的套接字。接下来用简单的write()和read()可以进行简单的收发数据。

基于TCP/IP的客户端
搭建过程:
1.创建一个socket,用函数socket()
2.设置要连接的对方的IP地址和端口等属性
3.连接服务器,用函数connect()
4.收发数据,用函数send()和recv(),或者
read()和write()
5.关闭网络连接

案例:

void ask_server(int socketfd)
{
    char buf[1024];

    while (1)
    {
        fgets(buf, 1024, stdin);

        if (strncmp(buf, "end", 3) == 0)
        {
            break;
        }

        write (socketfd, buf, strlen(buf));

        int ret = read (socketfd, buf, 1023);
        buf[ret] = '\0';

        printf ("buf : %s\n", buf);
    }
}


int main()
{   
    // 创建与服务器通信的套接字
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd == -1)
    {
        perror ("socket");
        return -1;
    }

    // 连接服务器
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family  = AF_INET;     // 设置地址族
    addr.sin_port    = htons(PORT); // 设置本地端口
    inet_aton("127.0.0.1",&(addr.sin_addr));


    // 连接服务器,如果成功,返回0,如果失败,返回-1
    // 成功的情况下,可以通过socketfd与服务器进行通信
    int ret = connect(socketfd, (struct sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror ("connect");
        return -1;
    }

    printf ("成功连上服务器\n");

    ask_server(socketfd);

    // 关闭套接字
    close(socketfd);

    return 0;
}

客户端在创建完与服务器端通信的socket后可以使用connect函数与服务器进行连接,在连接成功后,同样也可以使用write()函数和read()函数进行通信。

这样就完成了客户端与服务器的通信,那么客户端如何与客户端通信呢?其实很简单,客户端只要将目标客户端的信息发送给服务器,服务器通过目标客户端的信息获得目标客户端的socket,然后将消息发给该socket的客户端就可以了,在这里就不多说了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值