高并发服务器编写
大小端
大端:(Big-Endian):就是把数值的高位字节放在内存的低位地址上,把数值的低位字节放在内存的高位地址上。
小端:(Little-Endian):就是把数值的高位字节放在高位的地址上,低位字节放在低位地址上。
- 通常在计算机中为小端
- 网络里面为大端
测试
serv_addr.sin_port = htons(8193);
printf("%d\n", htons(SERVER_PORT));
输出为288
>>> bin(8193)[2:].zfill(16)
'0010000000000001'
>>> bin(288)[2:].zfill(16)
'0000000100100000'
c中的函数
#include <arpa/int.h>
- inet_pton
- inet_ntop
大小端转化
普通转化(unsing int -> int)
#include <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);
ip转化(string -> int)
#include <arpa/inet.h>
- int inet_pton(int af, const char *src, void *dst);
- const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
p -> 字符串, n -> network
ntop
需要多传socklen_t
sockaddr
sockaddr在头文件#include <sys/socket.h>
中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了,如下:
struct sockaddr {
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address 包含套接字中的目标地址和端口地址 */
};
但由于历史原因, bind
, accept
, connect
函数传参需要强制转化为其指针
sockaddr_in
由于sockaddr
ip和端口区分不够细致, 因此用sockaddr_in
来定义, 但传参需要强制转化为sockaddr
struct sockaddr_in serv_addr, clie_addr;
sockaddr_in
在头文件#include<netinet/in.h>
或#include <arpa/inet.h>
中定义
struct sockaddr_in {
short sin_family; //address family
u_short sin_port; //16 bit TCP/UDP port number
struct in_addr sin_addr; //32 bit IP address
char sin_zero[8]; //not use, for align
};
其中in_addr
结构体定义了32bit的ip地址, 定义如下
//
// IPv4 Internet address
// This is an 'on-wire' format structure.
//
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
sock
lfd = socket(AF_INET, SOCK_STREAM, 0);
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
- 第一个参数为地址族,
AF_INET``AF_INET6
常用(为”Address Family INET是“Inetnet”) - type 为数据传输方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM,在《socket是什么意思》一节中已经进行了介绍
- protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
bind
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
accept
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
connect
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 返回新的socket文件描述符
- 第三个参数为传入传出参数
socklen_t
和int
差不多
BUFSIZ
宏定义了, 大小为8k
server.c
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <ctype.h>
#include <arpa/inet.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8193
int main(){
int lfd, cfd;
struct sockaddr_in serv_addr, clie_addr;
socklen_t clie_addr_len;
char buf[BUFSIZ];
int n;
lfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
printf("%d\n", htons(SERVER_PORT));
// serv_addr.sin_addr.s_addr = htonl(SERVER_IP);
serv_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(lfd, 128);
clie_addr_len = sizeof(clie_addr);
cfd = accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
while(1){
n = read(cfd, (struct sockaddr *)buf, sizeof(buf));
for (int i = 0; i < n; i++)
buf[i] = toupper(buf[i]);
write(cfd, buf, n);
}
close(lfd);
close(cfd);
return 0;
}