《UNIX环境高级编程》笔记--套接字数据传输

套接字端点表示为文件描述符,只要建立连接,就可以使用read和write来通过套接字通信。在套接字描述符上采用read和

write是非常有意义的,因为可以传递套接字描述符到那些原来设计为处理本地文件的函数。而且可以安排传递套接字描述

符到执行程序的子进程,该子进程并不解释套接字。

但是如果想指定选项,从多个客户端接受数据报或者发送带外数据,则需要采用六个传递数据的套接字函数中的一个。

最简单的send,它和write很像,但是可以指定标志来改变处理传输数据的方式。

#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flag); //成功则返回发送的字节数,出错则返回-1.
send支持第四个参数flags。下表总结了这些标志:


send成功返回仅保证数据已经无错误地发送到网络上,但并一定能被另一端接受到。

对于支持为报文设限的协议,如果单个报文超过协议所支持的最大长度,send失败并将errno设为EMSGSIZE;对于字节流协议,

send会阻塞知道整个数据被传输。

函数sendto和send很类似,区别在于sendto允许在无连接的套接字上指定一个目标地址

#include<sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen);
//成功则返回发送的字节数,若出错则返回-1.
对于面向连接的套接字,目的地址是忽略的,因为目的地址蕴含在连接中。对于无连接的套接字,不能使用send,除非在

调用connect时预先设定了目标地址,或者采用sendto来提供另一种发送报文方式。

可以使用不止一个的选择来通过套接字发送数据,可以调用带有msghdr结构的sendmsg来制定多重缓冲区传输数据。

#include<sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); //成功则返回发送的字节数,出错则返回-1.
POSIX.1定义msghdr结构,至少应该有如下成员:

struct msghdr{

void *msg_name; //optional address

socklen_t msg_namelen; //address size in bytes

struct iovec *msg_iov; //array of IO buffers

int msg_iovlen; //number of elemnets in array;

void *msg_control; //ancillary data;

socklen_t msg_controllen; //number of ancillary bytes;

int msg_flags; //flags for received message;

......

};


函数recv和read很像,但是允许指定选项来控制如何接受数据。

#include<sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags); 
//返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,若出错则返回-1.
下图总结了flags的值:


可以使用recvfrom函数来得到数据发送者的源地址。

#include<sys/socket.h>
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen *restrict addrlen);
//返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,出错则返回-1.
如果addr非空,它将包含数据发送者的套接字端点地址。当调用recvfrom时,需要设置addrlen参数指向一个包含addr所指的套接字缓冲

区字节大小的整数。返回时,该整数设为该地址的实际字节大小。

因为可以获得发送者的地址,recvfrom通常用于无连接套接字。

为了将接收到的数据送入多个缓冲区,或者想接受辅助数据,可以使用recvmsg。

#include<sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
//返回值:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0,若出错则返回-1.
返回时,msghdr结构中的msg_flags字段被置为所接受数据的各种特征,具体如下表。



实践:

1.面向连接的服务器和客户端:

server.c  运行在ip地址为192.168.18.249的机器上

#include<stdio.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>

#define MAX_SIZE 256

int main(void){
        int listenfd,connfd;
        struct sockaddr_in servaddr,clientaddr;
        int n,addrlen = sizeof(struct sockaddr_in);
        char addr_p[16];
        char rbuf[MAX_SIZE];
        char sbuf[MAX_SIZE] = "i am server.";

        if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){
                perror("inet_pton");
                return -1;
        }
        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5555);

        if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
                perror("listen");
                return -1;
        }

        if(bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) == -1){
                perror("bind");
                return -1;
        }

        if(listen(listenfd, 10) == -1){
                perror("listen");
                return -1;
        }

        while(1){
                if((connfd = accept(listenfd, (struct sockaddr*)&clientaddr,(socklen_t*)&addrlen)) == -1){
                        perror("accept");
                        continue;
                }
                break;
        }

        if(inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr,addr_p,(socklen_t)sizeof(addr_p)) == NULL){
                perror("inet_ntop");
                return -1;
        }
        printf("connect client ip:%s\n",addr_p);
        n = recv(connfd, rbuf, MAX_SIZE, 0);
        if(n == -1){
                perror("recv");
                return -1;
        }
        rbuf[n] = '\0';
        printf("%s\n",rbuf);

        n = send(connfd, sbuf, strlen(sbuf)+1, 0);
        if(n == -1){
                perror("send");
                return -1;
        }
        close(connfd);
        close(listenfd);
        return 0;
}

client.c

#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>

#define MAX_SIZE 256

int main(void){
        int sockfd,n;
        char rbuf[MAX_SIZE],sbuf[MAX_SIZE]="i am client.";
        struct sockaddr_in servaddr;

        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5555);
        if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){
                perror("inet_pton");
                return -1;
        }

        if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
                perror("socket");
                return -1;
        }


        if(connect(sockfd,(struct sockaddr*)&servaddr,(socklen_t)sizeof(servaddr))<0){
                perror("connect");
                return -1;
        }

        if(send(sockfd, sbuf, strlen(sbuf)+1, 0) < 0){
                perror("send");
                return -1;
        }

        if((n =recv(sockfd,rbuf,MAX_SIZE,0)) < 0){
                perror("send");
                return -1;
        }
        rbuf[n]='\0';
        printf("%s\n",rbuf);
        close(sockfd);

        return 0;
}
先运行server,再运行client:

server:

connect client ip:192.168.18.25
i am client.

client:

i am server.


2.不面向连接的服务器和客户端:

server.c  运行在ip地址为192.168.18.249的机器上

#include<stdio.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>

#define MAX_SIZE 256

int main(void){
        int sockfd;
        struct sockaddr_in servaddr,clientaddr;
        int n,addrlen = sizeof(struct sockaddr_in);
        char addr_p[16];
        char rbuf[MAX_SIZE];

        if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){
                perror("inet_pton");
                return -1;
        }
        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5555);

        if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
                perror("listen");
                return -1;
        }

        if(bind(sockfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) == -1){
                perror("bind");
                return -1;
        }

        n = recvfrom(sockfd, rbuf, MAX_SIZE, 0, (struct sockaddr*)&clientaddr, (socklen_t*)&addrlen);
        if(n == -1){
                perror("recvfrom");
                return -1;
        }
        if(inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr,addr_p,(socklen_t)sizeof(addr_p)) == NULL){
                perror("inet_ntop");
                return -1;
        }
        printf("receive host ip:%s\n",addr_p);
        rbuf[n] = '\0';
        printf("%s\n",rbuf);

        close(sockfd);
        return 0;
}

client.c

#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>

#define MAX_SIZE 256

int main(void){
        int sockfd,n;
        char rbuf[MAX_SIZE],sbuf[MAX_SIZE]="i am client.";
        struct sockaddr_in servaddr;

        memset(&servaddr,0,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(5555);
        if(inet_pton(AF_INET,"192.168.18.249",&servaddr.sin_addr) < 0){
                perror("inet_pton");
                return -1;
        }

        if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
                perror("socket");
                return -1;
        }


        if(connect(sockfd,(struct sockaddr*)&servaddr,(socklen_t)sizeof(servaddr))<0){
                perror("connect");
                return -1;
        }

        if(send(sockfd, sbuf, strlen(sbuf)+1, 0) < 0){
                perror("send");
                return -1;
        }

        if((n =recv(sockfd,rbuf,MAX_SIZE,0)) < 0){
                perror("send");
                return -1;
        }
        rbuf[n]='\0';
        printf("%s\n",rbuf);
        close(sockfd);

        return 0;
}
先运行server,再运行client

server端显示:

receive host ip:192.168.18.25
i am client.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值