主要函数
-
结构体 sockaddr
表示通用socket地址
*结构体 sockaddr_in(in_addr) 、sockaddr_in6(in6_addr)
ipv4、ipv6专用地址结构体 -
函数 inet_pton
讲字符串表示ip地址转换成网络字节序整数表示的ip地址。 -
函数 socket
调用系统创建socket -
函数 bind
将一个socket与socket地址绑定。也称命名 -
函数listen
socket命名后需要创建一个监听队列以存放待处理的客户连接。 -
函数accept
调用listen监听队列中接受一个连接。 -
函数connect
客户端通过系统调用主动与服务器建立连接 -
函数close
关闭对应的socket -
函数recv、send
tcp数据读写
读取、写入sockfd上的数据 -
函数recvfrom、sendto
udp数据读写
读取、写入sockfd上的数据 -
函数recvmsg、sendmsg
通用数据读写 -
函数getsockname、getpeername
获取sockfd对应的本端socket地址
获取sockfd对应的远端socket地址 -
函数getsockopt、setsockopt
专门读取和设置socket文件描述符属性的方法
主机字节序和网络字节序
理解字节序可以参考阮一峰老师的文档:
http://www.ruanyifeng.com/blog/2016/11/byte-order.html
举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11
- 大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。
- 小端字节序:低位字节在前,高位字节在后,即以0x1122形式储存。
检查机器的字节序
#include <stdio.h>
void byteorder()
{
union
{
short value;
char union_bytes[sizeof(short)];
} test;
test.value = 0x0102;
if ((test.union_bytes[0] == 1) && test.union_bytes[1] == 2)
{
printf("big endian\n");
}
else if ( (test.union_bytes[0] == 2) && (test.union_bytes[1] == 1))
{
printf("little endian\n");
}
else
{
printf('unknown..\n');
}
}
int main() {
//printf("Hello, World!\n");
byteorder();
return 0;
}
现在pc大多采用小端字节序,因此小端字节序又被称为主机字节序。
大端也称为网络字节序。
即使是同一台机器上的两个进程通信,也要考虑字节序的问题。
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
static bool stop = false;
//SIGTERM信号处理函数,触发时结束主程序中的循环
static void handle_term( int sig )
{
stop = true;
}
int main( int argc, char* argv[] )
{
signal( SIGTERM, handle_term );
if( argc <= 3 )
{
printf( "usage: %s ip_address port_number backlog\n", basename( argv[0] ) );
return 1;
}
const char* ip = argv[1];
int port = atoi( argv[2] );
int backlog = atoi( argv[3] );
//创建一个socket PF_INET:ipv4 SOCK_STREAM:流服务
int sock = socket( PF_INET, SOCK_STREAM, 0 );
//判断是否失败 失败返回-1并设置errno 成功返回socket文件描述符
assert( sock >= 0 );
//创建一个专用 IPV4 socket 地址
struct sockaddr_in address;
//bzero功能:置字节字符串address的前sizeof( address )个字节为零且包括‘\0’
bzero( &address, sizeof( address ) );
address.sin_family = AF_INET;//地址组
//inet_pton:将用字符串表示ip地址转换成网络字节序整数表示的ip地址。 成功返回1 失败返回0
inet_pton( AF_INET, ip, &address.sin_addr );
//小端转大端 、主机字节序转换网络字节序
address.sin_port = htons( port );
//命名socket 将address所指的地址分配给未命名的 sock 变量(文件描述符)
int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
// bind 成功返回0 失败返回-1 并设置errno
assert( ret != -1 );
//监听socket sock参数:指定被监听的socket backlog参数:提示内核监听队列的最大长度
ret = listen( sock, backlog );
//listen 成功返回0 失败返回-1 并设置errno
assert( ret != -1 );
//循环等待连接,直到有SIGTERM信号将他中断。
while ( ! stop )
{
sleep( 1 );
}
//关闭socket
close( sock );
return 0;
}
在B主机
./程序名 192.168.30.109 12345 5
记得防火墙开启12345端口(如果是afw)
season@ubuntu:~$ sudo ufw allow 12345
主机A执行十次:
telnet 192.168.30.109 12345
发现执行6次后,一直处于等待中。
主机B:
netstat -nt |grep 12345
出现6(backlog值+1)个ESTABLISHED
accept用法
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc <= 2) {
printf("usage: %s id_address prot_number \n", basename(argv[0]));
return 1;
}
const char *ip = argv[1];
//字符串转换成整型数的一个函数
int port = atoi(argv[2]);
//创建一个专用 IPV4 socket 地址
struct sockaddr_in address;
//bzero功能:置字节字符串address的前sizeof( address )个字节为零且包括‘\0’
bzero(&address, sizeof(address));
address.sin_family = AF_INET;//地址组
//inet_pton:将用字符串表示ip地址转换成网络字节序整数表示的ip地址。 成功返回1 失败返回0
inet_pton(AF_INET, ip, &address.sin_addr);
//小端转大端 、主机字节序转换网络字节序
address.sin_port = htons(port);
//创建一个socket PF_INET:ipv4 SOCK_STREAM:流服务
int sock = socket(PF_INET, SOCK_STREAM, 0);
//判断是否失败 失败返回-1并设置errno 成功返回socket文件描述符
assert(sock >= 0);
//命名socket 将address所指的地址分配给未命名的 sock 变量(文件描述符)
int ret = bind(sock, (struct sockaddr *) &address, sizeof(address));
// bind 成功返回0 失败返回-1 并设置errno
assert(ret !=-1);
//监听socket sock参数:指定被监听的socket backlog参数:提示内核监听队列的最大长度
ret = listen(sock,5);
//listen 成功返回0 失败返回-1 并设置errno
assert(ret !=-1);
sleep(20);
//创建一个专用 IPV4 socket 地址
struct sockaddr_in client;
socklen_t client_addrlength = sizeof(client);
int connfd = accept(sock,(
struct sockaddr*)&client ,&client_addrlength);
if (connfd < 0)
{
printf("error is :%d\n",errno);
}
else
{
char remote[INET_ADDRSTRLEN];
printf("connected with ip: %s and port: %d \n",
inet_ntop(AF_INET,&client.sin_addr,remote,INET_ADDRSTRLEN),ntohs(client.sin_port));
close(connfd);
}
//关闭socket
close(sock);
return 0;
}
网友评论