Linux网络编程

来源:互联网 发布:数据库客户端如何使用 编辑:程序博客网 时间:2024/06/11 22:20

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的客户端就可以了,在这里就不多说了。