SOCKET socket(int af, int type, int protocol)
af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
返回值是socket的ID值,标识socket的唯一性,是一个整形数。
struct sockaddr_in{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
uint16_t sin_port; //16位的端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用,一般用0填充
};
struct in_addr{
in_addr_t s_addr; //32位的IP地址
};
struct sockaddr{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
char sa_data[14]; //IP地址和端口号
};
结构体sockaddr和sockaddr_in等价,sockaddr_in包含详细的ip地址和端口号,所以sockaddr_in更加利于阅读和使用,编程的时候多使用它。
bind:
int bind(SOCKET sock, const struct sockaddr *addr, int addrlen)
将套接字和ip地址和端口绑定。
connect:
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen)
各参数和bind函数一样,区别在于,connect函数是client端用于和server端建立连接。
listen:
int listen(SOCKET sock, int backlog);
sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。
accept:
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。
它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。
accept() 返回一个新的套接字来和客户端通信,addr 保存了客户端的IP地址和端口号,而 sock 是服务器端的套接字,大家注意区分。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。
最后需要说明的是:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
int setsockopt( int socket, int level, int option_name,
const void *option_value, size_t option_len);
第一个参数socket是套接字描述符。第二个参数level是被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET。 option_name指定准备设置的选项,option_name可以有哪些取值,这取决于level。
int reuse = -1 ;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); //防止关闭服务器后系统还没收回地址被占用
Linux系统提供了获取主机信息的函数,gethostbyname(),gethostbyaddr(),两者都会用到一个结构体,结构体信息如下:
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
}
#define h_addr h_addr_list[0] /* for backward compatibility */
#include < stdio.h >
#include < netdb.h >
#include < sys/socket.h >
#include < netinet/in.h >
#include < arpa/inet.h >
int main(int argc, char *argv[])
{
struct hostent *p;
int i;
if (argc < 2) return -1; p = gethostbyname(argv[1]); printf("hostname %s\n", p->h_name);
printf(“address “);
for (i = 0; p->h_addr_list[i]; i++)
{
printf(“%s “, inet_ntoa(*(struct in_addr *)p->h_addr_list[i]));
}
printf(“\n”);
return 0;
}
传参调用,参数长选项
void print_usage(const char *program_name)
{
printf(“\n%s — 1.0.0(2018.11.4)\n”, program_name);
printf(” Usage: %s -p [-h ]\n”, program_name);
printf(” -p –port the server listen port\n”) ;
printf(” -h –help the server file how to use\n”);
return ;
}
int opt = -1 ;
int port = 0 ;
const char *short_opts = “p:h”;
const struct option long_opts[] ={
{“help”, no_argument, NULL, ‘h’},
{ “port”, required_argument, NULL, ‘p’},
{0, 0, 0, 0}
};
while ((opt= getopt_long(argc, argv, short_opts, long_opts,NULL)) != -1)
{
switch (opt)
{
case ‘p’:
port = atoi(optarg);
break ;
case ‘h’:
print_usage(argv[0]) ;
return 0;
}
}
if( !port )
{
print_usage(argv[0]);
return 0;
}
服务器:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <getopt.h>
#include <stdlib.h>
#define BACKLOG 13
#define Sock_Port 9806
#define BUF_SIZE 1024
#define MSG_STR "Hello Client Welcome to Connect...!"
void print_usage(const char *program_name)
{
printf("\n%s -- 1.0.0(2018.11.4)\n", program_name);
printf(" Usage: %s -p <server_port> [-h <server_use_help>]\n", program_name);
printf(" -p --port the server listen port\n") ;
printf(" -h --help the server file how to use\n");
return ;
}
int main( int argc, char **argv )
{
int listen_fd,client_fd = -1 ;
struct sockaddr_in serv_addr ;
struct sockaddr_in cli_addr ;
socklen_t cli_addr_len ;
int rv = -1 ;
char buf[BUF_SIZE] ;
int opt = -1 ;
int port = 0 ;
const char *short_opts = "p:h";
const struct option long_opts[] = {
{"help", no_argument, NULL, 'h'},
{ "port", required_argument, NULL, 'p'},
{0, 0, 0, 0}
};
while ((opt= getopt_long(argc, argv, short_opts, long_opts,NULL)) != -1)
{
switch (opt)
{
case 'p':
port = atoi(optarg);
break ;
case 'h':
print_usage(argv[0]) ;
return 0;
}
}
if( !port )
{
print_usage(argv[0]);
return 0;
}
listen_fd = socket(AF_INET, SOCK_STREAM, 0) ;
if(listen_fd < 0)
{
printf("creat socket failure : %s \n", strerror(errno) ) ;
return -1 ;
}
printf("creat socket suceeful, listen_fd descriptor[%d]\n", listen_fd) ;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET ;
serv_addr.sin_port = htons(port) ;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY) ;
memset(&cli_addr, 0, sizeof(serv_addr));
int reuse = -1 ;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
if ( bind(listen_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr) ) < 0 )
{
printf("socket bind failure : %s\n", strerror(errno) ) ;
goto cleanup ;
}
if ( listen(listen_fd ,BACKLOG) < 0 )
{
printf("socket listen failure: %s\n", strerror(errno) ) ;
goto cleanup ;
}
while(1)
{
printf("\n/*********************************************************************/\n") ;
printf("Server Start Work, Server port:%d, And Waiting The Client Connect ...\n", port) ;
client_fd = accept(listen_fd,(struct sockaddr*) &cli_addr, &cli_addr_len) ;
if(client_fd < 0)
{
printf("accept failure: %s\n", strerror(errno)) ;
return -1 ;
}
printf("accept succeful, client [%s:%d] \n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port) ) ;
memset(buf, 0, sizeof(buf)) ;
rv = read (client_fd, buf, sizeof(buf)) ;
if(rv < 0)
{
printf("read from client failure: %s\n", strerror(errno)) ;
goto cleanup ;
}
if(rv == 0)
{
printf("client connect failure!\n") ;
continue ;
}
printf("\n %d byte read from client : '%s' " ,rv, buf) ;
/*
pid_t pid ;
pid = fork() ;
if( pid < 0 )
{
printf("Create fork failure:%s\n", strerror(errno)) ;
return 0 ;
}
if( pid > 0 )
{
if (write(client_fd, MSG_STR, strlen(MSG_STR) ) < 0)
{
printf("write to client failed: %s\n", strerror(errno)) ;
close(client_fd) ;
continue ;
}
}
if(pid == 0)
{
memset(buf, 0, sizeof(buf)) ;
rv = read (client_fd, buf, sizeof(buf)) ;
if(rv < 0)
{
printf("read from client failure: %s\n", strerror(errno)) ;
goto cleanup ;
}
if(rv == 0)
{
printf("client connect failure!\n") ;
continue ;
}
printf("\n %d byte read from client : '%s' " ,rv, buf) ;
}
*/
if (write(client_fd, MSG_STR, strlen(MSG_STR) ) < 0)
{
printf("write to client failed: %s\n", strerror(errno)) ;
close(client_fd) ;
continue ;
}
sleep(2) ;
close(client_fd) ;
}
cleanup:
close(listen_fd) ;
close(client_fd) ;
return 0 ;
}
客户端
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <stdlib.h>
#include <netdb.h>
#define BUF_SIZE 1024
#define MSG_STR "Hello Server, I connecting..."
void print_usage(const char *program_name)
{
printf("\n%s -- 1.0.0(2018.11.4)\n", program_name);
printf(" Usage:(1) %s -i <server_ip> -p <server_port> [-h <server_use_help>]\n", program_name);
printf(" (2) %s -n <server_hostname> -p <server_port> [-h <server_use_help>]\n", program_name);
printf(" -i --ip the server ip_address you want to connect\n") ;
printf(" -p --port the server listen port you want to connect\n") ;
printf(" -n --hostname the server hostname you want to connect\n") ;
printf(" -h --help the client file how to use\n");
printf(" Notice: If use hostname to connect Server ,Information get Segmentation fault, Please check out the hostname is ture you input!\n");
return ;
}
int main(int argc, char** argv)
{
int conn_fd ;
struct sockaddr_in serv_addr ;
char buf[BUF_SIZE] ;
int rv = -1 ;
int opt = -1 ;
int port = 0 ;
char* ip = NULL ;
char* hostname = NULL;
struct hostent *hostnp;
const char *short_opts = "n:i:p:h";
const struct option long_opts[] = {
{"help", no_argument, NULL, 'h'},
{"hostname", required_argument,NULL,'n'},
{"port", required_argument, NULL, 'p'},
{"ip", required_argument, NULL, 'i'},
{0, 0, 0, 0},
};
while ((opt= getopt_long(argc, argv, short_opts, long_opts,NULL)) != -1)
{
switch (opt)
{
case 'p':
port = atoi(optarg);
break ;
case 'i':
ip = optarg ;
break ;
case 'n':
hostname = optarg ;
break ;
case 'h':
print_usage(argv[0]) ;
return 0;
}
}
if( ( ( !port) || (!hostname) ) && ( (!ip) || (!port) ) )
{
print_usage(argv[0]);
return 0;
}
memset(buf, 0, sizeof(buf)) ;
if ( hostname )
{
hostnp = gethostbyname(hostname) ;
printf("hostname %s\n", hostnp->h_name);
ip = inet_ntoa( * (struct in_addr *)hostnp->h_addr );
printf("addr:%s\n",ip) ;
}
conn_fd = socket( AF_INET, SOCK_STREAM, 0 ) ;
if(conn_fd < 0)
{
printf("creat socket failure : %s\n", strerror(errno)) ;
return -1 ;
}
printf("socket suceeful,connect file descriptor[%d]\n" ,conn_fd) ;
memset(&serv_addr, 0, sizeof(serv_addr)) ;
serv_addr.sin_port = htons(port) ;
serv_addr.sin_family = AF_INET ;
inet_aton(ip, &serv_addr.sin_addr);
if( connect(conn_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0 )
{
printf("connect failure[%s:%d] : %s\n", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port), strerror(errno)) ;
goto cleanup ;
}
printf("connect server[%s:%d] succeful!\n ", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port)) ;
if(write(conn_fd, MSG_STR, strlen(MSG_STR)) < 0 )
{
printf("Write to server failed: %s\n", strerror(errno)) ;
goto cleanup ;
}
sleep(1) ;
memset(buf, 0, sizeof(buf)) ;
rv = read(conn_fd, buf, sizeof(buf)) ;
if(rv < 0)
{
printf("read from server failed: %s\n", strerror(errno)) ;
goto cleanup ;
}
printf("read %d byte from server:' %s'\n", rv, buf) ;
cleanup:
close(conn_fd) ;
return 0 ;
}
PS:端口用花生壳内网穿透