Unix编程-socket

Posted PorFavor

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unix编程-socket相关的知识,希望对你有一定的参考价值。

  套接字是一种进程间的通信的方法,不同于以往介绍的进程间通信方法的是,它并不局限于同一台计算机的资源,例如文件系统空间,共享内存或者消息队列。套接字可以认为是对管道概念的扩展——一台机器上的进程可以使用套接字与另一台机器上的进程通信。因此客户与服务器可以分散在网络中。同一台机器上的进程间也可以用套接字通信。套接字是一种通信机制,客户/服务器系统既可以在本地单机上运行,也可以在网络中运行。套接字与管道的区别:它明确区分客户与服务器,可以实现将多个客户连接到一个服务器。

 

     套接字的工作过程(服务器端):首先,服务器应用程序通过socket系统调用创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,不能与其他进程共享。其次,服务器进程使用bind系统调用给套接字命名。本地套接字的名字是linux文件系统的文件名,一般放在/tmp或者/usr/tmp 目录下。网络套接字的名字是与客户相连接的特定网络有关的服务标识符。此标识符允许linux将进入的针对特定端口号的连接转到正确的服务器进程。接下来,服务器进程开始等待客户连接到这个命名套接字,调用listen创建一个等待队列以便存放来自客户的进入连接。最后,服务器通过accept系统调用来接受客户的连接。此时,会产生一个与原有的命名套接字不同的新套接字,它仅用于与这个特定的客户通信,而命名套接字则被保留下来继续处理来自其他客户的连接。  



1.socket:打开一个网络接口

#include <sys/socket.h>

int socket(

    int domaine, /* AF_UNIX, AF_INET, AF_INET6 */

    int type, /* SOCK_DGRAM, SOCK_STREAM */

    int protocole /* 0: protocole par defaut */);

返回值:成功返回文件描述符,失败则返回-1

参数:

domiane

type : 对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。

protocole :所使用的协议,默认设为0;

 

int bind(

    int descripteur, /* descripteur de lasocket */

    struct sockaddr *adresse, /* pointeur vers l'adresse d'attachement */

    socklen_tlongueur_adresse /* longueur de l'adresse en octets */);

返回值:成功返回0,失败返回-1

参数:

descripteur : socket的文件描述符

 

ipv4对应的是:

#include <netinet/in.h>

struct sockaddr_in { /*adresse Internet d'une socket */

    sa_family_t sin_family; /* - domaine : AF_INET */

    uint16_t sin_port; /* - numéro de port sur 16 bits */

    struct in_addr sin_addr; /* - structured'adresse Internet */ };

 

ipv6

struct sockaddr_in6 {

    sa_family_t     sin6_family;   /* AF_INET6 */

    in_port_t      sin6_port;     /* port number */

    uint32_t        sin6_flowinfo; /* IPv6 flow information */

    struct in6_addrsin6_addr;     /* IPv6 address */

    uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */

};

 

struct in6_addr {

    unsignedchar  s6_addr[16];   /* IPv6 address */

};

 

Unix

#define UNIX_PATH_MAX   108

struct sockaddr_un {

    sa_family_tsun_family;               /* AF_UNIX */

    char        sun_path[UNIX_PATH_MAX];  /* pathname */

};

 


最常用的ipv4的实际写法(服务器)

struct sockaddr_in saddr = {0};//initialiser

    saddr.sin_family= AF_INET;

   saddr.sin_addr.s_addr = htonl(INADDR_ANY);

    saddr.sin_port = htons(PORT_SERVEUR);

 

客户端:

struct sockaddr_in saddr = {0};

  saddr.sin_family = AF_INET;

  saddr.sin_port= htons(PORT_SERVEUR);

  saddr.sin_addr.s_addr= inet_addr(argv[1]);

 


 

4.网络字节序

ntohs() — network-to-host 16 bit

htons() — host-to-network 16 bit

ntohl() — network-to-host 32 bit

htonl() — host-to-network 32 bit

 

5.listen

如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求

#include <sys/socket.h>

    int listen(

    int descripteur, /* descripteur de la socket d'ecoute */

    struct nb_pendantes /* nombre maximum de connexions pendantes */);

 

返回值:成功返回0,失败返回-1

参数:

descipteur : 所监听的socket文件描述符

nb_pendantes:相应socket可以排队的最大连接个

 

6.connect

作为客户端,通过connect与服务器的socket连接。connect会自动阻塞,直到客户端与服务器连接为止。

#include <sys/socket.h>

int connect(

    int descripteur, /* descripteur de socket de l'appelant */

    struct sockaddr *adresse, /*pointeur vers l'adresse de l'appelé */

    socklen_t longueur_adresse /* longueur de l'adresse de l'appelé */);

 

返回值:成功返回0,失败返回-1

参数

descripteur :socket文件描述符


7.accept

如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。

#include <sys/socket.h>

    int accept(

    int descripteur, /* descripteur de la socket d'écoute */

    struct sockaddr *adresse, /*pointeur vers l'adresse de l'appelant */

    socklen_t *longueur_adresse /* pointeur vers la longueur de l'adresse */);

 

返回值 :成功返回连接套接字,失败返回-1

参数

descripteur : 服务器端的socket文件描述符

longeur_adresse : 它也是结果的参数,用来接受上述 adresse 的结构的大小的,它指明 adresse 结构所占有的字节个数。同样的,它也可以被设置为NULL

设置adresse结果参数的写法:

struct sockaddr_in caddr = {0};

socklen_t calen = sizeof(caddr); //client addrese length

int sclient = accept(secoute,(struct sockaddr*) &caddr,&calen);

 

不设置的写法:

int sclient = accept(secoute,NULL,NULL);

 

(其中secoute是服务器的socket)

 

8.shutdown

#include <sys/socket.h>

    int shutdown(

    int descripteur, /* descripteur de la socket */

    int sens /* 0 : lecture, 1 :écriture, 2 : les deux */);

 

参数

descripteur : socket文件描述符,在服务器端则是由accept函数返回的连接套接字 ,在客户端则是最初定义的socket

sens : 单向或双向,SHUT_RD关闭读入通道,SHUT_WR关闭写入通道,SHUT_RDWR关闭双向

 

例题 :客户端向服务器发送一个小写字母,服务器将其升为大写字母并返回给客户

client.c

#define PORT_SERVEUR 6000

#define BUFFER_SIZE  1024           /* Taille maximum des messages */

int main(int argc, char **argv){ //127.0.0.1 identifiant

    int sclient =socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_insaddr = {0};

    saddr.sin_family= AF_INET;

    saddr.sin_port= htons(PORT_SERVEUR);

    saddr.sin_addr.s_addr= inet_addr(argv[1]);

    if(connect(sclient,(struct sockaddr*)&saddr,sizeof(saddr)) == -1){

        perror("connect erreur:");

        exit(1);

    }

 

    char commande[BUFFER_SIZE] = {0};

    while(commande[0] != '1'){

        printf("Bonjour %s, entrez des lettres miniscules, ou 1 pour sortir \n",argv[2]);

        fgets(commande,100,stdin);

        if(commande[0] == '1')

            break;

        write(sclient,commande,strlen(commande)+1);

        char c;

        int i = 0;

        printf("Messagereçu: ");

        for(i = 0;i<strlen(commande)+1;i++){

            if(read(sclient,&c,1) == 1)//réception d'un message du serveur pour vérifier le serveur a bien vu

                printf("%c",c);

        }

        printf("\n");

    }

   shutdown(sclient,SHUT_RDWR);

   close(sclient);

}

 

serveur.c

#define PORT_SERVEUR 6000

#define BUFFER_SIZE  1024           /* Taille maximum des messages */

int main(int argc, char **argv){

    int secoute;

    int sservice;

    struct sockaddr_insaddr = {0}, caddr = {0};//initialiser

    saddr.sin_family= AF_INET;

    saddr.sin_addr.s_addr = htonl(INADDR_ANY);

    saddr.sin_port = htons(PORT_SERVEUR);

    secoute = socket(AF_INET,SOCK_STREAM,0);

    if(secoute == 0){

        printf("Erreur creation socket \n");

        exit(1);

    }

    bind(secoute,(struct sockaddr*)&saddr,sizeof(saddr));

    listen(secoute,5);

 

    char commande[BUFFER_SIZE] = {0};

    while(1){

        printf("Serveur en attente de nouveaux clients. \n");

        socklen_tcalen = sizeof(caddr); //client addrese length

        sservice = accept(secoute,(struct sockaddr*) &caddr,&calen);

        while(commande[0] != '1'){

            printf("Client connecté. Serveur en attente de nouveaux messages. \n");

            memset(commande,'\0',BUFFER_SIZE); /* Mise a 0 du commande */

            int length_commande = read(sservice,commande,BUFFER_SIZE);

            if( commande[0] == '1'){

                printf("Client déconnecté.\n");

                break;

            }

            printf("Correction:");

            int i = 0;

            char c;

            for (i = 0;i<length_commande;i++){

                c =toupper(commande[i]);

               printf("%c",c);

               write(sservice,&c,1);

            }

            printf("\n");

        }

       shutdown(sservice,SHUT_RDWR);

        close(sservice);

    }

    close(secoute);

}

 




以上是关于Unix编程-socket的主要内容,如果未能解决你的问题,请参考以下文章

UNIX环境高级编程 学完以后 有啥用? 应用范围是啥?

unix高级环境编程看了能写项目吗

Unix环境高级编程书上的第一个例程如何运行(附代码)

读过 Unix网络编程 或者 熟知Unix网络编程的 的进来看一下

在下想用UNIX开始练编程,应该用啥版本?

Unix下网络编程概述