Socket
- socket 概念:
什么是socket?- socket 可以看成是用户j进程与内核网络协议栈的编程接口。
- socket 不仅用于本机进程间的通信,还可以用于网络上不同主机的进程间的通信
IPv4套接口地址结构
- IPv4套接口地址结构也称为“网际套接字地址结构”, 它以“sockaddr_in”命名,定义在头文件中
- 结构体如下
struct sockaddr_in { uint8_t sin_len; //(这个成员在有的内核中已经不使用了,根据实际情况来定,下面有介绍怎么查 ip 结构的具体内容)sin_len: 整个 sockaddr_in结构体的长度 sa_family_t sin_family; // sin_family指定该地址家族,在这里必须设置为AF_INET(它代表TCP协议) in_port_t sin_port; //端口 struct in_addr sin_addr; //IPv4的地址 char sin_zero[8]; //暂不使用,一般将其设置为0 };
通用地址结构
- 通用地址结构用来指定与套接字关联的地址
- 结构体如下
struct sockaddr { unit_8 sin_len; //sin_len 整个 sockaddr 结构体的长度 sa_family_t sin_family; //指定该地址家族 char sa_data[14]; //sin_family 决定它的形式 }
- 为什么会有两个 IP 地址结构?
- 因为是先具体定义是使用那个协议的结构,然后再将其强制转换为通用结构,再传给socketapi函数
- 可以用 man 7 ip 来查看这个函数的用法
sa_data[14] 就是 in_port_t sin_port、struct in_addr sin_addr char sin_zero[8]这三成员的大小
- 可以这样认为,这两种结构体是通用的
端口号的理解
有这样一种情况
- 一客户端的应用程序和服务器的应用程序通信,如图
服务器如何判断客户是要和那个应用程序通信
- 首先,要确定是哪一个主机的ip,
- 然后,再确定主机的哪一个应用程序即端口号。
- 比如app2-1对应的端口号是8001和app1,app2-2对应的端口号是8002对应app2
- 所以可以这样理解,应用程序就是通过端口号来区分的,对端口号发信息。
网络字节序
- 大端字节序(Big Endian)
- 最高有效位储存于最低内存地址处,最低有效位储存于最高内存地址处。
- 小端字节序(little Endian)
- 最高有效位储存于最高内存地址处,最低有效位储存于最低内存地址处。
- 主机字节序
- 不同的主机有不同的字节序,如x86为小段字节序, Motorola 6800 为大端字节序,ARM 字节序是可配置的。
- 网络字节序
- 网络字节序规定为大端字节序
- 用程序判别主机是大端字节序、还是小端字节序
- 代码如下
#include <stdio.h>
void main()
{
unsigned int data = 0x12345678;
char *p = &data;
printf("%x %x %x \n, p[0], p[1], p[2], p[3]");
if(p[0] == 0x78)
{
printf("The System is Little Endian");
}
else
{
printf("The System is Big Endian");
}
}
字节序转换函数
- uint32_t htonl(uint32_t hostlong);
- uint16_t htons(uint16_t hostshort);
- uint32_t ntohl(uint32_t netlong);
- uint16_t ntohs(uint16_t netshort);
说明:这里h代表host;n代表networks;s代表short;L代表long
将上面代码字节序转换为网络字节序
#include <stdio.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);
* /
#include <arpa/inet.h> ///Byte order conversion header file
void main()
{
unsigned int data = 0x12345678;
char *p = &data;
printf("%x %x %x \n", p[0], p[1], p[2], p[3]);
if(p[0] == 0x78)
{
printf("The System is Little Endian");
}
else
{
printf("The System is Big Endian");
}
printf("Converting local byte order into network byte order");
uint32_t mydata = htonl (data);
p = &mydata;
printf("%x %x %x \n, q[0], q[1], q[2], q[3]");
if(p[0] == 0x78)
{
printf("The network byte order is Little Endian");
}
else
{
printf("The network byte order is Big Endian");
}
}
为什么有地址转换函数
因为在tcp、ip协议里面 ipv4 是32bit,需要将其转换成类似与192.168.xxx.xxx的点分ip,或者将点分ip转换成32bit的ip
1. #include <netinet/in.h>
2. #include <arpa/inet.h>
3. int inet_aton(const char *cp, struct in_addr *inp); //把stuct转换成char *即把点分十进制转换成一个结构(32位的数)
4. int_addr_t inet_addr(const char *cp); //把点分十进制转换成一个32位的数
5. char *inet_ntoa(struct in_addr in); //把结转换成一个char *即把一个结构(32位的数)转换成点分十进制
#include <sys/socket.h> //Byte order conversion header file
#include <netinet/in.h> //adress conversion function
#include <arpa/inet.h>
void main()
{
/* int inet_aton(const char *cp, struct in_addr *inp); //1
in_addr_t inet_addr(const char *cp); //2
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in); //4 分配一个元素,这里就是一个传递结构体成员值的过程,所以这里不用传地址
struct in_addr inet_makeaddr(int net, int host);
in_addr_t inet_lnaof(struct in_addr in);
*/
}
test 4
#include <sys/socket.h> //Byte order conversion header file
#include <netinet/in.h> //adress conversion function
#include <arpa/inet.h>
void main()
{
/* int inet_aton(const char *cp, struct in_addr *inp); //1
* in_addr_t inet_addr(const char *cp); //2
* in_addr_t inet_network(const char *cp);
* char *inet_ntoa(struct in_addr in); //3
* struct in_addr inet_makeaddr(int net, int host);
* in_addr_t inet_lnaof(struct in_addr in);
*/
in_addr_t myinet = inet_addr("192.168.23.1"); //2
printf("%u\n", myinet);
/* Internet address.
* struct in_addr
* {
* uint32_t s_addr; //address in network byte order
* };
*/
struct in_addr myaddr;
inet_aton("192.168.23.1", &myaddr); //1
printf("%u\n", myaddr.s_addr);
char *inet_ntoa(myaddr); //4
printf("%s\n", inet_ntoa(myaddr));
套接字类型
- 流式套接字(SOCK_STREAM)
- 提供面向连接的、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收。
- 数据报式套接字(SOCK_DGRAM)
- 提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱
- 原始套接字(SOCK_RAW)
SocketApi 基本编程思想
Tcp客户/服务器模型
服务器
-
socket的简单实现:服务器接收到客户端的数据,再回馈给客户端
-
socket函数:
1.1 包含头文件
1.2功能:创建一个套接字用于通信
1.3原型:int socket(int domain, int type, int protocol);
1.4参数:- domain:指定通信协议族(protocol family)
- type:指定socket类型,流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAM
- protocol:协议类型
1.5 返回值:成功则返回非负整数,失败则返回-1.
1.6 man socket查询帮助
-
-
bind函数:
2.1 包含头文件
2.2功能:绑定一个本地地址到套接字
2.3原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
2.4参数:- sockfd:socket函数返回的套接字
- addr:要绑定的地址
- addrlen:地址长度
2.5返回值:成功返回0;失败返回-1
2.6man bind来查询用法
2.7几个特殊绑定的IP地址- INADDR_LOOPBACK (127.0.0.1)
- INADDR_ANY (0.0.0.0)
- INADDR_BROADCAST (255.255.255.255)
- listen函数
3.1 一般来说,listen函数应该在调用socket和bind函数之后,调用accept函数之前。
3.2对于给定的监听端口,内核要维护两个队列- 已由客户端发出并到达服务器,服务器正在等待完成相应的TCP三路握手过程
- 已完成连接的队列
- TCP为监听套接口维护的两个队列
- TCP三次握手和监听套口的两个队列
-
accept函数
4.1包含头文件
4.2功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞
4.2原型:int accept(int sockfd, struct sockaddr addr, socklen_t addrlen)
4.3参数:- sockfd:未连接套接字
- addr:要连接套接字的地址
- addrlen:第二个参数addr长度
4.4返回值:成功返回0;失败返回-1
服务器端实现代码
/*********************************************************************************
* Copyright: (C) 2018 anzhihong<[email protected]>
* All rights reserved.
*
* Filename: socket_server.c
* Description: This file is socket_server
*
* Version: 1.0.0(07/23/2018)
* Author: anzhihong <[email protected]>
* ChangeLog: 1, Release initial version on "07/23/2018 08:37:11 PM"
*
********************************************************************************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
/*
man socket
int socket(int domain, int type, int protocol);
*/
int sockfd = 0;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fun socket\n");
exit(0);
}
/*
man 7 ip
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//Internet address.
struct in_addr
{
uint32_t s_addr; //address in network byte order
};
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(8001);
srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/*
man 2 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
{
perror("fun socket\n");
exit(0);
}
/*
man listen
int listen(int sockfd, int backlog);
*/
//Once you call listen function,this socket will become passive,it means that can only recive and doesn't send actively
if(listen(sockfd, SOMAXCONN) < 0)
{
perror("fun socket\n");
exit(0);
}
//struct sockaddr peeraddr; //Universal ip
//socklen_t peerlen;
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//man 2 accept
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
struct sockaddr_in peeraddr; //tcpip address structure
socklen_t peerlen = sizeof(peerlen);
unsigned int conn = 0;
//Accept will return a new connection, it is
conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -1)
{
perror("fun socket\n");
exit(0);
}
printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
char rvbuf[1024] ;
while(1)
{
/*
man 2 read
size_t read(int fd, void *buf, size_t count);
*/
memset(rvbuf, 0, sizeof(rvbuf));
int ret = read(conn, rvbuf, sizeof(rvbuf));
if(ret == 0)
{
//If the other party is already closed during the reading process,tcpip will return a zero packet
printf("peer is already closed\n");
exit(0);
}
fputs(rvbuf, stdout); // server receives the data and prints the screen.
/*
man 2 write
size_t write(int fd, const void *buf, size_t count);
*/
write(conn, rvbuf, strlen(rvbuf)); //server sends pocket back
memset(rvbuf, 0, sizeof(rvbuf));
}
return 0;
}
客户端
-
connect函数
5.1包含头文件#include
5.2功能:建立一个连接至addr 所指定的套接字
5.3原型:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
5.4参数:- sockfd未连接套接字
- addr:要连接的套接字地址
- addrlen:第二个参数addr长度
5.5返回值:成功返回0,失败返回-1
客户端实现代码:
/*********************************************************************************
* Copyright: (C) 2018 anzhihong<[email protected]>
* All rights reserved.
*
* Filename: socket_server.c
* Description: This file is socket_client
*
* Version: 1.0.0(07/23/2018)
* Author: anzhihong <[email protected]>
* ChangeLog: 1, Release initial version on "07/23/2018 08:37:11 PM"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
/*
man socket
int socket(int domain, int type, int protocol);
*/
int sockfd = 0;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fun socket\n");
exit(0);
}
/*
man 7 ip
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//Internet address.
struct in_addr
{
uint32_t s_addr; //address in network byte order
};
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(8001);
srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//man connect
//int connect(int sockfd, const struct sockaddr *addr, socklen_t
if(connect(sockfd, (struct sockaddr *)(&srvaddr), sizeof(srvaddr)) < 0)
{
perror("fun socket\n");
exit(0);
}
//printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
char revbuf[1024] = {0};
char sendbuf[10244] = {0};
//man fgets
//char *fgets(char *s, int size, FILE *stream);
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
//Write data to the server
write(sockfd, sendbuf, strlen(sendbuf)); //server send message back
//Read data to the server
read(sockfd, revbuf, sizeof(revbuf));
//Print
fputs(revbuf, stdout);
memset(revbuf, 0, sizeof(revbuf));
memset(sendbuf, 0, sizeof(sendbuf));
}
close(sockfd);
return 0;
}
可以自己做个测试:
- 在客户端与服务器建立起连接后;
- 用命令 netstat -na | grep 8001 来查看监听的端口状态
- 然后先断开服务器。再用netstat -na | grep 8001查看
- 然后再用客户端发送消息,再查看8001的状态
到这里,简单的socket服务通信的大体框架搭建完毕。
问题1:在发送两个以上的字符后,再发送空字符,服务器仍然会将最后一个字符发送客户端,发送三个字符,会将最后两个发送给客户端?
这里是因为在使用完rvbuf后,没有清除,发送的 \n 覆盖了第一个字符,所以出现这样的情况。
问题2:端口,ip地址固定,更改不便。(通过传参解决,后面介绍)
问题3:先将服务器关闭,客户端不管,再启动服务器,无法启动
-
解决这个问题需要用到Socket API中的地址复用
- 服务器端尽可能使用SO_REUSEADDR
- 在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。使用SO_REUSEADDR选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器
- 地址复用函数 man setsockopt
- 头文件:#include
- 功能:可以不必等待客户机再关闭,就可以重启服务器。
- 原型:int getsockopt(int sockfd, int level, int optname, void optval, socklen_t optlen); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
-
参数:
- sockfd 未连接套接字
- level 设置级别(指定选项代码类型)
- optname(选项名): 选项名称
- optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ }
- optlen(选项长度) :optval 的大小
-
返回值:成功返回0,不成功返回-1.
将函数放到bind之前
int optval = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval,sizeof(optlen)) < 0 ) // int optname 在man 7 ip里面查找
{
perror("fun socket\n");
exit(0);
}
if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
{
perror("fun socket\n");
exit(0);
}
- 这样问题就解决了
- 问题4:服务器一对多的实现????(多并发)
- 创建多进程:
- 代码实现
利用fork函数,来实现服务器一对多的功能
服务器
*********************************************************************************
* Copyright: (C) 2018 anzhihong<[email protected]>
* All rights reserved.
*
* Filename: socket_server.c
* Description: This file is socket_server
*
* Version: 1.0.0(07/23/2018)
* Author: anzhihong <[email protected]>
* ChangeLog: 1, Release initial version on "07/23/2018 08:37:11 PM"
*
********************************************************************************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
/*
man socket
int socket(int domain, int type, int protocol);
*/
int sockfd = 0;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fun socket\n");
exit(0);
}
/*
man 7 ip
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//Internet address.
struct in_addr
{
uint32_t s_addr; //address in network byte order
};
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(8001);
srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/*
man 2 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
{
perror("fun socket\n");
exit(0);
}
/*
man listen
int listen(int sockfd, int backlog);
*/
//Once you call listen function,this socket will become passive,it means that can only recive and doesn't send actively
if(listen(sockfd, SOMAXCONN) < 0)
{
perror("fun listen\n");
exit(0);
}
//struct sockaddr peeraddr; //Universal ip
//socklen_t peerlen;
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//man 2 accept
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
struct sockaddr_in peeraddr; //tcpip address structure
socklen_t peerlen = sizeof(peerlen);
unsigned int conn = 0;
while(1)
{
//Accept will return a new connection, it is
//If there is no client connection, it will block here
conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -1)
{
perror("fun conn\n");
exit(0);
}
printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
int pid = fork();
if(pid == 0) //this process is child process
{
close(sockfd); // child process doesn't need to listen
char rvbuf[1024] = {0};
while(1)
{
/*
man 2 read
size_t read(int fd, void *buf, size_t count);
*/
int ret = read(conn, rvbuf, sizeof(rvbuf));
if(ret == 0)
{
//If the other party is already closed during the reading process,tcpip will return a zero packet
printf("peer is already closed\n");
exit(0);
}
else if(ret < 0)
{
printf("read data failure\n");
exit(0);
}
fputs(rvbuf, stdout); // server receives the data and prints the screen.
/*
man 2 write
size_t write(int fd, const void *buf, size_t count);
*/
write(conn, rvbuf, sizeof(ret)); //server sends pocket back
}
}
else if(pid > 0) //parent process
{
close(conn); //parent doesn't need connection
}
else
{
printf("create fork failure");
exit(0);
}
}
close(sockfd);
close(conn);
return 0;
}
- 实现服务器给客户端发送信息
- 代码如下:
客户端
/*********************************************************************************
* Copyright: (C) 2018 anzhihong<[email protected]>
* All rights reserved.
*
* Filename: socket_client.c
* Description: This file is socket_client
*
* Version: 1.0.0(07/23/2018)
* Author: anzhihong <[email protected]>
* ChangeLog: 1, Release initial version on "07/23/2018 08:37:11 PM"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
/*
man socket
int socket(int domain, int type, int protocol);
*/
int sockfd = 0;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fun socket\n");
exit(0);
}
/*
man 7 ip
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//Internet address.
struct in_addr
{
uint32_t s_addr; //address in network byte order
};
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(8002);
srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//man connect
//int connect(int sockfd, const struct sockaddr *addr, socklen_t
if(connect(sockfd, (struct sockaddr *)(&srvaddr), sizeof(srvaddr)) < 0)
{
perror("fun socket\n");
exit(0);
}
//printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
char revbuf[1024];
char sendbuf[1024];
//man fgets
//char *fgets(char *s, int size, FILE *stream);
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL);
{
memset(revbuf, 0, sizeof(revbuf));
memset(sendbuf, 0, sizeof(sendbuf));
printf("sendfbuf:%s ", sendbuf);
//Write data to the server
write(sockfd, sendbuf, strlen(sendbuf)); //server send message back
//Read data to the server
read(sockfd, revbuf, sizeof(revbuf));
//Print
fputs(revbuf, stdout);
memset(revbuf, 0, sizeof(revbuf));
memset(sendbuf, 0, sizeof(sendbuf));
}
close(sockfd);
return 0;
}
服务端
/*********************************************************************************
* Copyright: (C) 2018 anzhihong<[email protected]>
* All rights reserved.
*
* Filename: socket_server.c
* Description: This file is socket_server
*
* Version: 1.0.0(07/23/2018)
* Author: anzhihong <[email protected]>
* ChangeLog: 1, Release initial version on "07/23/2018 08:37:11 PM"
*
********************************************************************************/
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
/*
man socket
int socket(int domain, int type, int protocol);
*/
int sockfd = 0;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("fun socket\n");
exit(0);
}
/*
man 7 ip
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//Internet address.
struct in_addr
{
uint32_t s_addr; //address in network byte order
};
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(8002);
srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/*
man 2 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
{
perror("fun socket\n");
exit(0);
}
/*
man listen
int listen(int sockfd, int backlog);
*/
//Once you call listen function,this socket will become passive,it means that can only recive and doesn't send actively
if(listen(sockfd, SOMAXCONN) < 0)
{
perror("fun listen\n");
exit(0);
}
//struct sockaddr peeraddr; //Universal ip
//socklen_t peerlen;
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//man 2 accept
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
struct sockaddr_in peeraddr; //tcpip address structure
socklen_t peerlen = sizeof(peerlen);
unsigned int conn = 0;
while(1)
{
//Accept will return a new connection, it is
conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
if(conn == -1)
{
perror("fun conn\n");
exit(0);
}
printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
int pid = fork();
if(pid == 0) //this process is child process
{
close(sockfd); //close sockfd
char rvbuf[1024];
memset(rvbuf,0, sizeof(rvbuf));
while(1)
{
/*
man 2 read
size_t read(int fd, void *buf, size_t count);
*/
int ret = read(conn, rvbuf, sizeof(rvbuf));
printf("rvbuf is:%s\n", rvbuf);
if(ret == 0)
{
//If the other party is already closed during the reading process,tcpip will return a zero packet
printf("peer is already closed\n");
exit(0);
}
else if(ret < 0)
{
printf("read data failure\n");
exit(0);
}
fputs(rvbuf, stdout); // server receives the data and prints the screen.
/*
man 2 write
size_t write(int fd, const void *buf, size_t count);
*/
//write(conn, rvbuf, ret); //server sends pocket back
memset(rvbuf, 0, sizeof(rvbuf));
fgets(rvbuf, sizeof(rvbuf), stdin);
write(conn, rvbuf, ret); //server sends pocket back
//printf("rvbuf is:%s\n", &rvbuf);
}
}
else if(pid > 0) //parent process
{
close(conn); //parent doesn't need connection
}
else
{
printf("create fork failure");
exit(0);
}
}
close(sockfd);
close(conn);
return 0;
}
- 建立连接后,想知道到客户端自己的IP地址??
- 函数原型:int getsockname(int sockfd, struct sockaddr addr, socklen_t addrlen);
/*********************************************************************************
* Copyright: (C) 2018 anzhihong<[email protected]>
* All rights reserved.
*
* Filename: socket_client.c
* Description: This file is socket_client
*
* Version: 1.0.0(07/23/2018)
* Author: anzhihong <[email protected]>
* ChangeLog: 1, Release initial version on "07/23/2018 08:37:11 PM"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#define ERR_EXIT(m) \
do \
{\
perror(m);\
exit(EXIT_FAILURE); \
} while(0)
int main()
{
/*
man socket
int socket(int domain, int type, int protocol);
*/
int sockfd = 0;
//sockfd = socket(AF_INET, SOCK_STREAM, 0);
sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd == -1)
{
//perror("fun socket\n");
//exit(0);
ERR_EXIT("socket");
}
/*
man 7 ip
struct sockaddr_in
{
sa_family_t sin_family; //address family: AF_INET
in_port_t sin_port; //port in network byte order
struct in_addr sin_addr; //internet address
};
//Internet address.
struct in_addr
{
uint32_t s_addr; //address in network byte order
};
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
*/
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(8001);
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*intet_aton("127.0.0.1", &server.sin_addr);*/
//man connect
//int connect(int sockfd, const struct sockaddr *addr, socklen_t
if(connect(sockfd, (struct sockaddr *)(&srvaddr), sizeof(srvaddr)) < 0)
{
//perror("fun socket\n");
//exit(0);
ERR_EXIT("fun socket");
}
//获取本地的地址,即端口号的地址(必须是已经建立好的套接口连接才能调用这个函数)
struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if(getsockname(sockfd, (struct sockaddr*)&localaddr, &addrlen) < 0)
ERR_EXIT("getsockname");
printf("ip = %s\n port = %d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));
char revbuf[1024] = {0};
char sendbuf[10244] = {0};
int ret;
//man fgets
//char *fgets(char *s, int size, FILE *stream);
while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
//Write data to the server
write(sockfd, sendbuf, strlen(sendbuf)); //server send message back
//Read data to the server
ret = read(sockfd, revbuf, sizeof(revbuf));
if(ret == -1)
{
ERR_EXIT("read");
break;
}
else if(ret == 0)
{
ERR_EXIT("server is closed");
break;
}
//Print
fputs(revbuf, stdout);
memset(revbuf, 0, sizeof(revbuf));
memset(sendbuf, 0, sizeof(sendbuf));
}
close(sockfd);
return 0;
}
- 获取主机名
- 函数原型:int gethostname(char *name, size_t len);
- 返回对应于给定主机名的主机信息:
- 函数原型:struct hostent gethostbyname(const char name);
/*********************************************************************************
* Copyright: (C) 2018 anzhihong<[email protected]>
* All rights reserved.
*
* Filename: getip.c
* Description: This file is get ip
*
* Version: 1.0.0(07/23/2018)
* Author: anzhihong <[email protected]>
* ChangeLog: 1, Release initial version on "07/23/2018 08:37:11 PM"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int getlocalip(char *ip)
{
char host[100] = {0};
if(gethostname(host, sizeof(host)) < 0)
return -1;
struct hostent *hp;
if((hp = gethostbyname(host)) == NULL)
return -1;
//strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr_list[0]));
strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr));
printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[0])); //这里虽然是char **h_addr_list类型,但内部保存的是IP地址的结构,所以要强制转换成 *(struct int_addr*) 结构体
return 0;
}
int main(void)
{
char host[100] = {0};
if(gethostname(host, sizeof(host)) < 0)
ERR_EXIT("gethostname");
struct hostent *hp;
if((hp = gethostbyname(host)) == NULL)
ERR_EXIT("gethostbyname");
int i = 0;
while(hp->h_addr_list[i] != NULL) //遍历所有地址。这里的char **h_addr_list;保存的地址并不是点分十进制
{
printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[i])); //这里虽然是char **h_addr_list类型,但内部保存的是IP地址的结构,所以要强制转换成 *(struct int_addr*) 结构体。
i++;
}
char ip[16] = {0};
getlocalip(ip);
printf("localip = %s\n", ip);
return 0;
}