创建 socket
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:使用哪个协议簇,PF_UNIX: UNIX本地域协议族 PF_INET: TCP/IPv4协议族 PF_INET6: TCP/IPv6协议族
type:指定服务类型。TCP:使用SOCK_STREAM(流服务);UDP:使用SOCK_UGRAM(数据报)
protocol:在前两个确定的情况下,选择一个具体的协议,一般都是0,表示使用默认协议。
return:成功返回一个socket文件描述符,失败则返回-1并设置errno。
命名 socket
#include <sys/socket.h> int bind(int socket, const struct sockaddr *address, socklen_t address_len);
所谓命名,就是用bind来给socket绑定地址。
地址:sockaddr和sockaddr_in大小一致,sockaddr端口和IP地址融合在一起了,后者(sockaddr_in)是前者(sockaddr)的细化。
include <netinet/in.h> struct sockaddr { unsigned short sa_family; // 2 bytes address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address }; // IPv4 AF_INET sockets: struct sockaddr_in { short sin_family; // 2 bytes e.g. AF_INET, AF_INET6 unsigned short sin_port; // 2 bytes e.g. htons(3490) struct in_addr sin_addr; // 4 bytes see struct in_addr, below char sin_zero[8]; // 8 bytes zero this if you want to }; struct in_addr { unsigned long s_addr; // 4 bytes load with inet_pton() };
return:成功返回0,失败则返回-1并设置errno。
监听 socket
#include <sys/socket.h> int listen(int socket, int backlog);
socket:被监听的socket文件描述符
backlog:指定内核监听队列的最大长度,如果超过这个值,将不接受新的连接,客户端会收到ECONNREFUSED错误信息,在内核2.2版本之前,backlog = 连接状态(ESTABLISH)+ 半连接状态(SYN_RCVD),但是内核2.2版本之后等于完全连接状态的socket的上限。socket被接受连接后,将从队列中取出,也就是accept。
return:成功返回0,失败则返回-1并设置errno。
接受连接 socket
#include <sys/socket.h> int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
socket:被监听的socket文件描述符。
address:用来获取被接受连接的远端socket地址。
address_len:指定socket地址的长度。
return:成功返回客户端的socket,失败则返回-1并设置errno。
demo
#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; 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 ", basename( argv[0] ) ); return 1; } const char* ip = argv[1]; int port = atoi( argv[2] ); int backlog = atoi( argv[3] ); // 1.创建socket // int domanin: 协议族: PF_UNIX: UNIX本地域协议族 PF_INET: TCP/IPv4协议族 PF_INET6: TCP/IPv6协议族 // int type: SOCK_STREAM(流服务):TCP SOCK_UGRAM(数据报):UDP int sock = socket( PF_INET, SOCK_STREAM, 0 ); assert( sock >= 0 ); // 2.确定socket地址 struct sockaddr_in address; bzero( &address, sizeof( address ) ); // 地址族:AF_UNIX: UNIX本地域协议族 AF_INET: TCP/IPv4协议族 AF_INET6: TCP/IPv6协议族 address.sin_family = AF_INET; inet_pton( AF_INET, ip, &address.sin_addr ); // 转换为网络字节序:大段 address.sin_port = htons( port ); // 3.命名socket int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) ); assert( ret != -1 ); // 4.监听socket ret = listen( sock, backlog ); assert( ret != -1 ); while ( ! stop ) { sleep( 1 ); } close( sock ); return 0; }
执行
./5-3testlisten 10.120.225.64 12345 5 # 监听12345 backlog使用5 telnet 10.120.225.64 12345 & netstat -nt | grep 12345
结果
netstat -nt | grep 12345 tcp 0 0 10.120.225.64:12345 10.143.44.169:63208 SYN_RECV tcp 0 0 10.120.225.64:12345 10.120.225.64:52750 SYN_RECV tcp 0 0 10.120.225.64:52750 10.120.225.64:12345 ESTABLISHED tcp 0 0 10.120.225.64:12345 10.120.225.64:52746 ESTABLISHED tcp 0 0 10.120.225.64:52746 10.120.225.64:12345 ESTABLISHED tcp 0 0 10.120.225.64:52748 10.120.225.64:12345 ESTABLISHED tcp 0 0 10.120.225.64:52745 10.120.225.64:12345 ESTABLISHED tcp 0 0 10.120.225.64:12345 10.120.225.64:52745 ESTABLISHED
demo2:添加accept
while ( ! stop ) { struct sockaddr_in client; socklen_t client_addrlength = sizeof( client ); printf("bloack wait for accept client..... "); int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength ); if (connfd < 0) { printf( "errno is: %d ", errno ); } else { char remote[INET_ADDRSTRLEN ]; // 将二进制整数地址转为点分字符串形式,结果存在remote,INET_ADDRSTRLEN:16 IPV4地址大小 // 成功返回指向remote的地址,失败则返回NULL const char* ptr = inet_ntop( AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN ); // 将网络字节序转为主机字节序,网络字节序是大端, int port = ntohs( client.sin_port ); printf( "connected with ip: %s and port: %d ", ptr, port); printf( "remote: %p and ptr: %p ", remote, ptr); close(connfd); } sleep( 1 ); }
结果:
[zf@localhost 5]$ ./5-3testlisten 192.168.1.104 1234 5 bloack wait for accept client..... connected with ip: 192.168.1.103 and port: 57735 remote: 0x7fffcfb52da0 and ptr: 0x7fffcfb52da0 bloack wait for accept client.....