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的主要内容,如果未能解决你的问题,请参考以下文章