本文来自个人博客:https://dunkwan.cn
字节序
同一台主机上进程间通信是不需要考虑字节序问题的,但是在网络上的不同主机上进程进行通信时,则要考虑字节序问题。
字节序是一个处理器架构特性,用于指示像整数这样的大数据类型内部的字节如何排序。当处理器架构支持大端字节序,那么最大字节地址出现在最低有效字节上(Least Significant Byte, LSB)。不管字节如何排序,最高有效字节(Most Significant Byte, MSB)总是在左边,最低有效字节总是在右边。当处理器架构支持小端字节序时,则最低有效字节包含最小字节地址。如下面例子所示,可以对处理器的大小端进行判断。
#include <func.h>
void bigEndianOrLittleEndian()
{
int n = 0x04030201;
char *cp =(char *)&n;
if(cp[0] == 1 && cp[3] == 4)
printf("This platform is little endian.\n");
else if(cp[0] == 4 && cp[3] == 1)
printf("This platform is big endian.\n");
}
int main(void)
{
bigEndianOrLittleEndian();
return 0;
}
如上面所示,当一个整型数被强制转换成字符指针时,在小端处理器上,cp[0]指向最低有效字节因而包含1,cp[3] 指向最高有效字节因而包含4。相比较而言,在大端处理器上,cp[0]指向最高有效字节因而包含4,cp[3]指向最低有效字节因而包含1。下图展示了32位整数中的字节序。
socket
函数
socket
函数用于创建套接字
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
返回值:若成功,返回文件(套接字)描述符;若出错,返回-1。
domain
取值情形如下:
type
取值情形如下:
protocol
取值情形如下:通常为0,表示为给定的域和套接字类型选择默认协议。
connect
函数
connect
函数用于建立连接。
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
返回值:若成功,返回0;若出错,返回-1。
bind
函数
bind
函数用于关联地址和套接字。
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
返回值:若成功,返回0;若出错,返回-1。
listen
函数
listen
函数用于宣告服务器愿意接受连接请求。
#include <sys/socket.h>
int listen(int sockfd, int backlog);
返回值:若成功,返回0;若出错,返回-1。
发送数据函数
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
返回值:若成功,返回发送的字节数;若出错,返回-1。
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen);
返回值:若成功,返回发送的字节数;若出错,返回-1。
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
返回值:若成功,返回发送的字节数;若出错,返回-1。
接收数据函数
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
返回值:返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0;若出错,返回-1。
ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict addrlen);
返回值:返回数据字节长度;若无可用数据或对等方已经按序结束,返回0;若出错,返回-1。
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
返回值:返回数据的字节长度;若无可用数据或对等方已经按序结束,返回0;若出错,返回-1。
测试示例1:
面向连接的客户服务器模型。
/* client module */
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#define BUFLEN 128
extern int connect_retry(int ,int, int, const struct sockaddr *, socklen_t);
void print_uptime(int sockfd)
{
int n;
char buf[BUFLEN];
while((n = recv(sockfd, buf, BUFLEN, 0)) > 0)
write(STDOUT_FILENO, buf, n);
if(n < 0)
err_sys("recv error");
}
int main(int argc, char *argv[])
{
struct addrinfo *ailist , *aip;
struct addrinfo hint;
int sockfd, err;
if(argc != 2)
err_quit("usage: ruptime hostname");
memset(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_STREAM;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)
err_quit("getaddrinfo error: %s", gai_strerror(err));
for(aip = ailist; aip != NULL; aip = aip->ai_next){
if((sockfd = connect_retry(aip->ai_family, SOCK_STREAM, 0, aip->ai_addr,
aip->ai_addrlen)) < 0){
err = errno;
}else{
print_uptime(sockfd);
exit(0);
}
}
err_exit(err, "can't connect to %s", argv[1]);
}
/* server module*/
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>
#define BUFLEN 128
#define QLEN 10
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
extern int initserver(int, const struct sockaddr *, socklen_t, int);
void serve(int sockfd)
{
int clfd;
FILE *fp;
char buf[BUFLEN];
set_cloexec(sockfd);
for(;;){
if((clfd = accept(sockfd, NULL, NULL)) < 0){
syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno));
exit(1);
}
set_cloexec(clfd);
if((fp = popen("/usr/bin/uptime", "r")) == NULL){
sprintf(buf, "error: %s\n", strerror(errno));
send(clfd, buf, strlen(buf), 0);
}else{
while(fgets(buf, BUFLEN, fp) != NULL)
send(clfd, buf, strlen(buf), 0);
pclose(fp);
}
close(clfd);
}
}
int main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err, n;
char *host;
if(argc != 1)
err_quit("usage: ruptimed");
if((n = sysconf(_SC_HOST_NAME_MAX)) < 0)
n = HOST_NAME_MAX;
if((host = (char *)malloc(n)) == NULL)
err_sys("malloc error");
if(gethostname(host, n) < 0)
err_sys("gethostname error");
daemonize("ruptimed");
memset(&hint, 0, sizeof(hint));
hint.ai_flags = AI_CANONNAME;
hint.ai_socktype = SOCK_STREAM;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0){
syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err));
exit(1);
}
for(aip = ailist; aip != NULL; aip = aip->ai_next){
if((sockfd = initserver(SOCK_STREAM , aip->ai_addr, aip->ai_addrlen,
QLEN)) >= 0){
serve(sockfd);
exit(0);
}
}
exit(1);
}
测试示例2:
面向无连接客户服务器模型。
/* client module */
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <sys/socket.h>
#define BUFLEN 128
#define TIMEOUT 20
void sigalrm(int signo)
{
}
void print_uptime(int sockfd, struct addrinfo *aip)
{
int n;
char buf[BUFLEN];
buf[0] = 0;
if(sendto(sockfd, buf, 1, 0, aip->ai_addr, aip->ai_addrlen) < 0)
err_sys("sendto error");
alarm(TIMEOUT);
if((n = recvfrom(sockfd, buf, BUFLEN, 0, NULL, NULL)) < 0){
if(errno != EINTR)
alarm(0);
err_sys("recv error");
}
alarm(0);
write(STDOUT_FILENO, buf, n);
}
int main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err;
struct sigaction sa;
if(argc != 2)
err_quit("usage: ruptime hostname");
sa.sa_handler = sigalrm;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if(sigaction(SIGALRM, &sa, NULL) < 0)
err_sys("sigaction error");
memset(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_DGRAM;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0)
err_quit("getaddrinfo error: %s", gai_strerror(err));
for(aip = ailist; aip != NULL; aip = aip->ai_next){
if((sockfd = socket(aip->ai_family, SOCK_DGRAM, 0)) < 0){
err = errno;
}else{
print_uptime(sockfd, aip);
exit(0);
}
}
fprintf(stderr, "can't contact %s: %s\n", argv[1], strerror(err));
exit(1);
}
/* server module */
#include "apue.h"
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/socket.h>
#define BUFLEN 128
#define MAXADDRLEN 256
#ifdef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
extern int initserver(int, const struct sockaddr *, socklen_t, int);
void serve(int sockfd)
{
int n;
socklen_t alen;
FILE *fp;
char buf[BUFLEN];
char abuf[MAXADDRLEN];
struct sockaddr *addr = (struct sockaddr *)abuf;
set_cloexec(sockfd);
for(;;){
alen = MAXADDRLEN;
if((n = recvfrom(sockfd, buf, BUFLEN, 0, addr, &alen)) < 0){
syslog(LOG_ERR, "ruptimed: recvfrom error: %s", strerror(errno));
exit(1);
}
if((fp = popen("/usr/bin/uptime", "r")) == NULL){
sprintf(buf, "error: %s\n", strerror(errno));
sendto(sockfd, buf, strlen(buf), 0, addr, alen);
}else{
if(fgets(buf, BUFLEN, fp) != NULL)
sendto(sockfd, buf, strlen(buf), 0, addr, alen);
pclose(fp);
}
}
}
int main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err, n;
char *host;
if(argc != 1)
err_quit("usage: ruptimed");
if((n = sysconf(_SC_HOST_NAME_MAX)) < 0)
n = _SC_HOST_NAME_MAX;
if((host = (char *)malloc(n)) == NULL)
err_sys("malloc error");
if(gethostname(host, n) < 0)
err_sys("gethostname error");
daemonize("ruptimed");
memset(&hint, 0, sizeof(hint));
hint.ai_flags = AI_CANONNAME;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0){
syslog(LOG_ERR,"ruptimed: getaddrinfo error: %s", gai_strerror(err));
exit(1);
}
for(aip = ailist; aip != NULL; aip = aip->ai_next){
if((sockfd = initserver(SOCK_DGRAM, aip->ai_addr,
aip->ai_addrlen, 0)) >= 0){
serve(sockfd);
exit(0);
}
}
exit(1);
}