1 网络字节序
大端模式:若将数据的高字节保存在内存的低地址,将数据的低字节保存在内存的高地址;
小端模式:若将数据的高字节保存在内存的高地址,将数据的低字节保存在内存的低地址。
- 网络数据流:大端模式
- 计算机:大端/小端模式
Linux系统中提供了一些用于字节序转换的函数,这些函数存在于函数库arpa/inet.h中,它们的定义如下:
uint32_t htonl(uint32_t hostlong);//主机字节序转换为网络字节序(长整型)
uint16_t htons(uint16_t hostshort);//主机字节序转换为网络字节序(短整型)
uint32_t ntohl(uint32_t netlong);//网络字节序转换为主机字节序(长整型)
uint16_t ntohs(uint16_t netshort);//网络字节序转换为主机字节序(短整型)
- h:主机host;
- n:网络network;
- l:32位长整型;
- s:16位短整型;
- 若主机使用大端模式,则这些函数不做转换,将参数原样返回;
- 若主机采用小端模式,参数的字节顺序才会被修改。
2 IP地址转换函数
- 常见的IP地址格式类似“192.168.10.1”,这是一个标准的IPv4格式的地址,但这种格式是为了方便用户对其进行操作,若要使计算机能够识别,需先将其转换为二进制格式。
- 如今Linux编程中常用inet_pton()和inet_ntop()来转换IP地址,这两个函数不但能转换IPv4格式的地址in_addr,还能转换IPv6格式的地址in_addr6。
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);//先将字符串src转换为af地址族中的网络地址结构,再将转换后的网络地址结构存储到参数dst中,af必须为AF_INET或AF_INET
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);//网络地址转换为字符串
3 socket编程实例
【案例 1】编写C/S结构的程序,分别创建服务器和客户端,使用TCP协议实现。
- 客户端:从终端获取一个字符串发送给服务器,然后接收服务器返回的字符串并打印;
- 服务器:接收客户端发来的字符,将每个字符转换为大写再返回给客户端。
tcpclient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXLINE 80
#define SERV_PORT 6666
int main(int paraArgc, char *paraArgv[]) {struct sockaddr_in tempServAddr; //定义服务器地址结构体char tempBuf[MAXLINE];int tempSockFd, tempDataLen;char *tempStr;if (paraArgc != 2) {fputs("usage: ./client message\n", stderr);exit(1);}//of iftempStr = paraArgv[1];//创建客户端套接字文件tempSockFd = socket(AF_INET, SOCK_STREAM, 0);//初始化服务器端口地址bzero(&tempServAddr, sizeof(tempServAddr));tempServAddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &tempServAddr.sin_addr);tempServAddr.sin_port = htons(SERV_PORT);//请求链接connect(tempSockFd, (struct sockaddr *)&tempServAddr, sizeof(tempServAddr));//发送数据send(tempSockFd, tempStr, strlen(tempStr), 0);//接收客户端返回的数据tempDataLen = recv(tempSockFd, tempBuf, MAXLINE, 0);printf("Response from server:\n");//将客户端返回的数据打印到终端write(STDOUT_FILENO, tempBuf, tempDataLen);//关闭连接close(tempSockFd);return 0;
}//of maintcpserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 80 //最大数据长度
#define SERV_PORT 6666 //服务器端口号
int main(void) {struct sockaddr_in tempServAddr, tempCliAddr; //定义服务器与客户端地址结构体socklen_t tempCliAddrLen; //客户端地址长度int tempListenFd, tempConnFd;char tempBuf[MAXLINE];char tempStr[INET_ADDRSTRLEN];int i, tempDataLen;//创建服务器端套接字文件tempListenFd = socket(AF_INET, SOCK_STREAM, 0);//初始化服务器端口地址bzero(&tempServAddr, sizeof(tempServAddr)); //将服务器端口地址清零tempServAddr.sin_family = AF_INET;tempServAddr.sin_addr.s_addr = htonl(INADDR_ANY);tempServAddr.sin_port = htons(SERV_PORT);//将套接字文件与服务器端口地址绑定bind(tempListenFd , (struct sockaddr *)&tempServAddr, sizeof(tempServAddr));//监听,并设置最大连接数为20listen(tempListenFd , 20);printf("Accepting connections ...\n");//接收客户端数据,并处理请求while (1) {tempCliAddrLen= sizeof(tempCliAddr);tempConnFd = accept(tempListenFd, (struct sockaddr *)&tempCliAddr, &tempCliAddrLen);tempDataLen = recv(tempConnFd, tempBuf, MAXLINE, 0);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &tempCliAddr.sin_addr, tempStr, sizeof(tempStr)),ntohs(tempCliAddr.sin_port));for (i = 0; i < tempDataLen; i++){tempBuf[i] = toupper(tempBuf[i]);}//of for isend(tempConnFd, tempBuf, tempDataLen, 0);//关闭连接close(connfd);}//of whilereturn 0;
}//of main
【案例2】编写C/S结构的程序,分别创建服务器和客户端,使用UDP协议实现。
- 客户端:从终端获取一个字符串发送给服务器,然后接收服务器返回的字符串并打印;
- 服务器:接收客户端发来的字符,将每个字符转换为大写再返回给客户端。
udpclient.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <ctype.h>
#define MAXLINE 80
#define SERV_PORT 6666
int main(int paraArgc, char *paraArgv[]){struct sockaddr_in tempServAddr;int tempSockFd, tempDataLen;char tempBuf[MAXLINE];tempSockFd = socket(AF_INET, SOCK_DGRAM, 0);bzero(&tempServAddr, sizeof(tempServAddr));tempServAddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &tempServAddr.sin_addr);tempServAddr.sin_port = htons(SERV_PORT);//发送数据到客户端while (fgets(tempBuf, MAXLINE, stdin) != NULL) {tempDataLen = sendto(tempSockFd, tempBuf, strlen(tempBuf), 0, (struct sockaddr *)&tempServAddr, sizeof(tempServAddr));if (tempDataLen == -1) {perror("sendto error");}//of if//接收客户端返回的数据tempDataLen = recvfrom(tempSockFd, tempBuf, MAXLINE, 0, NULL, 0);if (tempDataLen == -1){perror("recvfrom error");}//of if//将接收到的数据打印到终端send(STDOUT_FILENO, tempBuf, tempDataLen , 0);}//of whileclose(tempSockFd);return 0;
}//of mainudpserver.c
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <ctype.h>
#define MAXLINE 80 //最大数据长度
#define SERV_PORT 6666 //服务器端口号
int main(void) {struct sockaddr_in tempServAddr, tempCliAddr; //定义服务器与客户端地址结构体socklen_t tempCliAddrLen; //客户端地址长度int tempSockFd; //服务器socket文件描述符char tempBuf[MAXLINE];char tempStr[INET_ADDRSTRLEN];int i, tempDataLen;tempSockFd = socket(AF_INET, SOCK_DGRAM, 0);//创建服务器端套接字文件//初始化服务器端口地址bzero(&tempServAddr, sizeof(tempServAddr)); //地址结构体清零tempServAddr.sin_family = AF_INET; //指定协议族tempServAddr.sin_addr.s_addr = htonl(INADDR_ANY);tempServAddr.sin_port = htons(SERV_PORT); //指定端口号//绑定服务器端口地址bind(tempSockFd, (struct sockaddr *)&tempServAddr, sizeof(tempServAddr));printf("Accepting connections ...\n");//数据传输while (1) {tempCliAddrLen = sizeof(tempCliAddr);//接收数据tempDataLen = recvfrom(tempSockFd, tempBuf, MAXLINE, 0, (struct sockaddr*)&tempCliAddr, &tempCliAddrLen);if (tempDataLen == -1){perror("recvfrom error");}//of ifprintf("received from %s at PORT %d\n",inet_ntop(AF_INET, &tempCliAddr.sin_addr, tempStr, sizeof(tempStr)),ntohs(tempCliAddr.sin_port));//服务器端操作,小写转大写for (i = 0; i < tempDataLen; i++){buf[i] = toupper(buf[i]);}//of for itempDataLen = sendto(tempSockFd, tempBuf, tempDataLen, 0, (struct sockaddr *)&tempCliAddr, sizeof(tempCliAddr));if (tempDataLen == -1){perror("sendto error");}//of if}//of whileclose(tempSockFd);return 0;
}//of main