Linux网络编程初步
主机字节序和网络字节序
32位置机器一次性能装载4字节。那么四字节在内存的顺序影响它被累加器装载成的整数的值。主要分为大端和小端。
大端字节序是一个整数高位字节(23~31bit)存在内存的低处,低字节(0~7 bit) 存储在内存的高地址处。小端相反。
现代PC大多是小端序。小端就称为主机字节序。JVM采用大端(网络字节序)。下面是检测大端小段的案例
我的linux机子是小端序了。。。
union {
short value;
char union_bytes[sizeof( short ) ];
} test;
test.value = 0x0102;
printf("%d %d\n", test.union_bytes[0], test.union_bytes[1]);
if(test.union_bytes[0] == 1 && test.union_bytes[1] == 2) {
puts("大端序");
} else if(test.union_bytes[0] == 2 && test.union_bytes[1] == 1) {
puts("小端序");
} else {
puts("未知");
}
linux下提供了四个函数来完成主机字节序和网络字节序的转换。
#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
例如:host to network long => htonl
一个网络编程的例子
服务端:
#include <bits/stdc++.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{
if ( argc <= 2 )
{
printf("usage: %s ip_address port_number\n", basename( argv[0] ));
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] );
// ===================================================================================================================
// 创建套接字
//创建地址的结构体
struct sockaddr_in address; // <netinet/in.h>
//* Structure describing an Internet socket address. */
// struct sockaddr_in
// {
// __SOCKADDR_COMMON (sin_);
// in_port_t sin_port; /* Port number. */
// struct in_addr sin_addr; /* Internet address. */
//
// /* Pad to size of `struct sockaddr'. */
// unsigned char sin_zero[sizeof (struct sockaddr) -
// __SOCKADDR_COMMON_SIZE -
// sizeof (in_port_t) -
// sizeof (struct in_addr)];
// };
bzero(&address, sizeof( address )); // <string.h> /* Set N bytes of S to 0. */
// extern void bzero (void *__s, size_t __n) __THROW __nonnull ((1));
// 里面的sin_zero数组通常清零,所以这么写
address.sin_family = AF_INET; // AF_INET代表IPV4地址族,PF_INET是对应的协议族,宏定义的值是一样的,理论上可以混用
inet_pton(AF_INET, ip, &address.sin_addr); // <arpa/inet.h>
//
// extern int inet_pton (int __af, const char *__restrict __cp,
// void *__restrict __buf) __THROW;
//
// /* Convert from presentation format of an Internet number in buffer
// starting at CP to the binary network format and store result for
// interface type AF in buffer starting at BUF. */
address.sin_port = htons( port ); // 大端字节序和小段字节序的转换 <netinet/in.h>
int sock = socket(PF_INET, SOCK_STREAM, 0); //创建套接字 <sys/socket.h>
assert( sock >= 0 ); //* Create a new socket of type TYPE in domain DOMAIN, using
// protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
// Returns a file descriptor for the new socket, or -1 for errors. */
// extern int socket (int __domain, int __type, int __protocol) __THROW;
// domain: 说明系统底层的协议族,对于TCP/IP 而言,改参数设置为PF_INET(IPV4),
// PF_INET6(IPV6), UNIX本地域协议簇,改参数设置为 PF_UNIX
// type: 服务类型 SOCK_STREAM(流服务),SOCK_UGRAM(数据报)
// 对于TCP,SOCK_STREAM 表示TCP, SOCK_DGRAM 表示
// protocol: 在前面的协议前提下的可选参数,一般给0就好
// ===================================================================================================================
// 命名socket
int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ));
assert( ret != -1 ); // /* Give the socket FD the local address ADDR (which is LEN bytes long). */
// extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
// __THROW;
// sock就是刚刚socket的返回值。可以理解为端口和IP绑定。
ret = listen( sock, 5 ); // 开始监听。
assert( ret != -1 ); // /* Prepare to accept connections on socket FD.
// N connection requests will be queued before further requests are refused.
// Returns 0 on success, -1 for errors. */
// extern int listen (int __fd, int __n) __THROW;
// 第二个参数是处于完全连接状态(ESTABLISHED)的 socket上线。
struct sockaddr_in client;
socklen_t client_addrlength = sizeof( client ); // types.h 是个 unsigned int
int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength);
// This function is a cancellation point and therefore not marked with
// __THROW. */
// extern int accept (int __fd, __SOCKADDR_ARG __addr,
// socklen_t *__restrict __addr_len);
//
if(connfd < 0)
{
printf("errno is: %d\n", errno);
}
else
{
char buffer[ BUF_SIZE ];
memset( buffer, '\0', BUF_SIZE );
ret = recv( connfd, buffer, BUF_SIZE - 1, 0 );
printf("[%d: %s]\n", ret, buffer);
memset( buffer, '\0', BUF_SIZE );
ret = recv( connfd, buffer, BUF_SIZE - 1, MSG_OOB );
printf("[%d: %s]\n", ret, buffer);
memset( buffer, '\0', BUF_SIZE );
ret = recv( connfd, buffer, BUF_SIZE - 1, 0 );
printf("[%d: %s]\n", ret, buffer);
close( connfd );
}
close( sock );
return 0;
}
客户端:
#include <bits/stdc++.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if(argc <= 2)
{
printf("usage: %s ip_address port_number\n", basename( argv[0] ));
// basename :
// Return the file name within directory of FILENAME. We don't
// declare the function if the `basename' macro is available (defined
// in <libgen.h>) which makes the XPG version of this function
// available.
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] );
printf("ip = %s port = %d\n", ip, port);
struct sockaddr_in server_address;
bzero(&server_address, sizeof( server_address ));
server_address.sin_family = AF_INET; //#define PF_INET 2 //IP protocol family. // PF_INET equal AF_INET
inet_pton( AF_INET, ip, &server_address.sin_addr );
server_address.sin_port = htons( port );
int sockfd = socket( PF_INET, SOCK_STREAM, 0 );
assert( sockfd >= 0 );
if( connect( sockfd, ( struct sockaddr* )&server_address, sizeof( server_address ) ) < 0 )
{
printf("connection failed\n");
}
else
{
const char *oob_data = "abc";
const char *normal_data = "123";
send(sockfd, normal_data, strlen(normal_data), 0);
send(sockfd, oob_data, strlen(oob_data), MSG_OOB);
send(sockfd, normal_data, strlen(normal_data), 0);
}
close(sockfd);
return 0;
}