Linux 进程间套接字通信(Socket)编程

Linux 进程间套接字通信(Socket)编程


转载链接:https://blog.csdn.net/violet_echo_0908/article/details/49670901


姓名:罗学元        学号:21181214375     学院:广州研究院


【嵌牛导读】Linux进程间套接字通信编程


【嵌牛鼻子】Linux 进程间套接字及通信编程


【嵌牛提问】Linux进程间套接字编程如何进行,包含哪些部分


一、流式socket的接口及作用

socket的接口函数声明在头文件

 

1.创建套接字——socket系统调用

该函数来创建一个套接字,并返回一个描述符,该描述符可以用来访问该套接字,其原型如下:

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

函数中的三个参数分别对应前面所说的三个套接字属性。protocol参数设置为0表示使用默认协议。

 

2.命名(绑定)套接字——bind系统调用

该函数把通过socket调用创建的套接字命名,从而让它可以被其他进程使用。对于AF_UNIX,调用该函数后套接字就会关联到一个文件系统路径名,对于AF_INF,则会关联到一个IP端口号。函数原型如下:

int bind(int socket, const struct sockaddr *address, size_t a ddress_len)

成功时返回0,失败时返回-1;

 

3.创建套接字队列(监听)——listen系统调用

该函数用来创建一个队列来保存未处理的请求。成功时返回0,失败时返回-1。其原型如下:

int listen(int socket, int backlog)

backlog用于指定队列的长度,等待处理的进入连续的个数最多不能超过这个数字,否则往后的连接将被拒绝,导致客户的连接请求失败。调用后,程序一直会监听这个IP端口,如果有连接请求,就把它加入到这个队列中。

4.接受连接——accept系统调用

该系统调用用来等待客户建立对该套接字的连接。accept系统调用只有当客户程序试图连接到由socket参数指定的套接字上时才返回,也就是说,如果套接字队列中没有未处理的连接,accept将阻塞直到有客户建立连接为止。accept函数将创建一个新套接字来与该客户进行通信,并且返回新套接字的描述符,新套接字的类型和服务器监听套接字类型是一样的。它的原型如下:


int accept(int socket, struct sockaddr *address, size_t *address_len)

1

address为连接客户端的地址,参数address_len指定客户结构的长度,如果客户地址的长度超过这个值,它将会截断。

 

5.请求连接——connect系统调用

该系统调用用来让客户程序通过在一个未命名套接字和服务器监听

套接字之间建立连接的方法来连接到服务器。它的原型如下:

int connect(int socket, const struct sockaddr *address, size_t

address_len)

参数socket指定的套接字连接到参数address指定的服务器套接字。

成功时返回0,失败时返回-1。

 

6.关闭socket——close系统调用

该系统调用用来终止服务器和客户上的套接字连接,我们应该总是在连接的两段(服务器和客户)关闭套接字。

 

二、进程使用流式socket进行通信

1.服务器程序:

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>


int main()

{

    int server_sockfd = -1;

    int client_sockfd = -1;

    int client_len = 0;

    struct sockaddr_in server_addr;

    struct sockaddr_in client_addr;


//创建流套接字

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);    

//设置服务器接收的连接地址和监听的端口

server_addr.sin_family = AF_INET;     //指定网络套接字

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);   //接受所有IP地址的连接

server_addr.sin_port = htons(9736);     //绑定到9736端口


//绑定(命名)套接字

    bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

listen(server_sockfd, 5);      //创建套接字队列,监听套接字

signal(SIGCHLD, SIG_IGN);      //忽略子进程停止或退出信号


    while(1){

        char ch = '\0';

        client_len = sizeof(client_addr);

        printf("Server waiting\n");


//接受连接,创建新的套接字

        client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);


        if(fork()==0){

//子进程中,读取客户端发过来的信息,处理信息,再发送给客户端

            read(client_sockfd, &ch, 1);

            sleep(5);

            ch++;

            write(client_sockfd, &ch, 1);

            close(client_sockfd);

        }

        else{

//父进程中,关闭套接字

            close(client_sockfd);

        }

    }

}

这是一个服务器程序,它首先创建套接字,然后绑定一个端口再监听套接字,忽略子进程的停止消息等,然后它进入循环,一直循环检查是否有客户连接到服务器,如果有,则调用fork创建一个子进程来处理请求。利用read系统调用来读取客户端发来的信息,利用write系统调用来向客户端发送信息。这个服务器的工作非常简单,就是把客户发过来的字符+1,再发送回给客户。

 

2.客户端程序:

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <signal.h>

#include <arpa/inet.h>

#include <stdio.h>

#include <stdlib.h>


int main()

{

    int sockfd = -1;

    int len = 0;

    struct sockaddr_in address;

    int result;

    char ch = 'A';


//创建流套接字

sockfd = socket(AF_INET, SOCK_STREAM, 0);     //设置要连接的服务器的信息

address.sin_family = AF_INET;     //使用网络套接字

address.sin_addr.s_addr = inet_addr("127.0.0.1");    //服务器地址

address.sin_port = htons(9736);      //服务器所监听的端口

    len = sizeof(address);

result = connect(sockfd, (struct sockaddr*)&address, len);    //连接到服务器


    if(result == -1)

    {

        perror("ops:client\n");

        exit(1);

    }


write(sockfd, &ch, 1);      //发送请求给服务器


read(sockfd, &ch, 1);       //从服务器获取数据

    printf("char from server = %c\n", ch);

    close(sockfd);

    exit(0);

}

这是一个客户程序,它同样要先创建套接,然后连接到指定IP端口服务器,如果连接成功,就用write来发送信息给服务器,再用read获取服务器处理后的信息,再输出。

 

 

 

三.函数解释

1.socket

功能:创建一个套接口()。

原型:

#include <winsock.h>

SOCKET PASCAL FAR socket( int af, int type, int protocol);

af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。


type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。


protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

SOCK_STREAM提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。

SOCK_DGRAM支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。

SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()和recv()传送数据。当会话结束后,调用closesocket()。带外数据根据规定用send()和recv()来接收。


实现SOCK_STREAM类型套接口的通讯协议保证数据不会丢失也不会重复。如果终端协议有缓冲区空间,且数据不能在一定时间成功发送,则认为连接中断,其后续的调用也将以WSAETIMEOUT错误返回。


SOCK_DGRAM类型套接口允许使用sendto()和recvfrom()从任意端口发送或接收数据报。如果这样一个套接口用connect()与一个指定端口连接,则可用send()和recv()与该端口进行数据报的发送与接收。

 

2.htonl(), ntohl(), htons(), ntohs() 函数

在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题。这是就可能用到htons(), ntohl(), ntohs(),htons()这4个函数。


网络字节顺序与本地字节顺序之间的转换函数:


htonl()–“Host to Network Long”

ntohl()–“Network to Host Long”

ntohs()–“Network to Host Short”


之所以需要这些函数是因为计算机数据表示存在两种字节顺序:NBO与HBO


网络字节顺序NBO(Network Byte Order): 按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。主机字节顺序(HBO,Host Byte Order): 不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。


如Intel x86结构下, short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 12


如IBM power PC结构下, short型数0x1234表示为12 34, int型数0x12345678表示为12 34 56 78


由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字节顺序,其实就是如同power pc那样的顺序. 在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换.

 

 

 

3.int socket(int domain, int type,int protocol)

domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等). AF_UNIX只能够用于单一的Unix系统进程间通信,而AF_INET是针对Internet的,因而可以允许在远程主机之间通信

type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等) SOCK_STREAM表明我们用的是TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信.


socket为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情况

 

 

 

4.int bind(int sockfd, struct sockaddr *my_addr, int addrlen)

sockfd:是由socket调用返回的文件描述符.

addrlen:是sockaddr结构的长度.

my_addr:是一个指向sockaddr的指针.

有sockaddr的定义

struct sockaddr

{

    unisgned short as_family;

    char sa_data[14];

};


不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替.在其中有sockaddr_in的定义


struct sockaddr_in{

    unsigned short sin_family;

    unsigned short int sin_port;

    struct in_addr sin_addr;

    unsigned char sin_zero[8];

}

我们主要使用Internet所以sin_family一般为AF_INET,sin_addr设置为INADDR_ANY表示可以和任何的主机通信,sin_port是我们要监听的端口号.sin_zero[8]是用来填充的. bind将本地的端口同socket返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket一样

 

 

 

5.int listen(int sockfd,int backlog)

sockfd:是bind后的文件描述符.

backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示可以介绍的排队长度.

listen函数将bind的文件描述符变为监听套接字.返回的情况和bind一样.

 

 

 

6.int accept(int sockfd, struct sockaddr *addr,int *addrlen)

sockfd:是listen后的文件描述符.

addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了.

bind,listen和accept是服务器端用的函数,accept调用时,服务器端的程序会一直阻塞到有一个客户程序发出了连接. accept成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了. 失败时返回-1

 

 

 

7.int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)

sockfd:socket返回的文件描述符.

serv_addr:储存了服务器端的连接信息.其中sin_add是服务端的地址

addrlen:serv_addr的长度


connect函数是客户端用来同服务端连接的.成功时返回0,sockfd是同服务端通讯的文件描述符失败时返回-1


总的来说网络程序是由两个部分组成的–客户端和服务器端.它们的建立步骤一般是:


服务器端

socket–>bind–>listen–>accept


客户端

socket–>connect

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容