TCP:(Transfer Control protocol,传输控制协议)提供面向连接的,一对一的可靠传输的协议
数据无误,数据不丢失,数据无失序
适用场景:
适合对传输质量要求较高,以及传输大量数据的通信
在需要可靠数据传输的场合,通常适用TCP协议
MSN/QQ等即时通讯软件的用户登录账号管理相关的功能通常采用TCP协议
UDP:(user Datagram Protocol,用户数据报协议):提供不可靠,无连接的传输协议
适用场景发送小尺寸数据(如对DNS服务器进行IP地址查询时) 在接收数据,给出应答较困难的网络中适用UDP(如无线网络) 适合于广播/组播式通信中 MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通常采用UDP协议 流媒体,VOD,VoIP,IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输
主机字节序到网络字节序:
u_long htonl(u_long hostlong);
u_short htons(u_short short);
网络字节序到主机字节序:
u_long ntohl(u_long hostlong);
u_short ntohs(u_short short);
inet_aton()
将strptr所指的字符串转换成32位的网络字节序二进制值
inet_addr()
功能同上,返回转换后的地址
仅适用于IPV4,出错时返回-1。
局限性:不能用于255.255.255.255的转换
inet_ntoa()将32位网络字节序二进制地址转换成点分十进制的字符串
inet_pton()
int inet_pton(int af, const char* src ,void* dst)
将IPV4/IPV6 的地址转换成binary格式
使用于IPV4/IPV6
能正确处理255.255.255.255的转换问题
参数:
1.地址协议族(AF_INET或AF_INET6)
2.src:是一个指针(填写分点形式的IP地址(主要指IPV4))
3.dst:转换的结果给到dst
inet_ntop(int af,const void *src,char *dst,socklen_t size)
把ipv4和ipv6的网络字节序变成本地的字符串形式的IP地址
参数:
1.af:地址协议族(AF_INET或AF_INET6)
2.src:是一个指针(32)
3.dst:输出结果为32位点分形式的IP地址
4.size:长度
int socket(int domain,int type,int protocol);
参数
domain:
AF_INET
AF_INET6
AF_UNIX,AF_LOCAL
AF_NETLINK
AF_PACKET
type:
SOCK_STREAM: 流式套接字,唯一对应于TCP
SOCK_DGRAM:数据报套接字,唯一对应着UDPSOCK_RAW:原始套接字
protocol:
一般填0,原始套接字编程时需填充
返回值:
成功返回文件描述符
出错返回-1
如果是IPV6编程,要使用struct sockddr_in6结构体(man 7 IPV6),通常使用struct sockaddr_storage来编程。
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
参数:
sockfd:通过socket()函数拿到的fd
addr:采用struct sockaddr的结构体地址,通用结构体
struct sockaddr{
sa_family_t sa_family;
char sa_data[4];
}
// 基于Internel通信结构体
struct sockaddr_in{
as_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
sin_zero , //填充字节,需清零
}
struct in_addr{
uint32_t s_addr;
}
addrlen:地址长度
int listen(int sockfd,int backlog);
参数:
sockfd: 通过socket()函数拿到的fd;
backLog:同时允许几路客户端和服务器进行正在连接的过程(正在三次握手),一般填5。
内核中服务器的套接字fd会维护2个链表
1.正在三次握手的客户端链表(数量=2*backlog+1)
2.已经建立好连接的客户端链表(已经完成三次握手分配好了的newfd)
返回值:
成功返回0; 出错返回-1
listen(fd,5);//表示系统允许11(2*5+1)个客户端同时进行三次握手
阻塞等待客户端连接请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd:经过前面socket()创建并通过bind(),listen()设置过的fd
addr:指向存放地址信息的结构体的首地址
获取客户端IP地址和端口号
addrlen:存放地址信息的结构体的大小
返回值:
成功,返回返回已经建立连接的新的newfd
出错,返回-1
int connect (int sockfd, struct sockaddr * serv_addr, int addrlen)
参数:
sockfd:通过socket()函数拿到的fd
addr:struct sockaddr的结构体变量地址
addrlen:地址长度
返回值:
成功,返回0
失败,返回-1
服务端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFSIZE 1024
#define QUIT_STR "QUIT"
#define SERV_IP 5001
#define SERV_IP_ADDR "192.168.224.132"
#define BACKLOG 5
void *client_data_handle( void *arg)
{
//强转为int型的指针,对它再取值
int newfd=*(int *) arg;
int ret = -1;
char buf[BUFSIZE];
printf(" child handle progess : newfd =%d\n",newfd);
while(1)
{
do
{
bzero(buf,BUFSIZE);
ret=read(newfd,buf,BUFSIZE-1);
}while(ret < 1);
if ( ! ret)
{
break;
}
printf(" recevice data :%s\n",buf);
//两个字符串做比较,不一样返回0,一样返回1,则执行下面的内容
if( ! strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) {
printf(" Client is exiting !\n");
break;
}
}
close(newfd);
return NULL;
}
void child_data_handle(int signum)
{
if( SIGCHLD == signum)
{
waitpid(-1,NULL,WNOHANG);
}
}
int main()
{
printf(" this is service!\n");
int fd = -1;
signal(SIGCHLD,child_data_handle);
struct sockaddr_in sin;
//1.socket
fd=socket(AF_INET,SOCK_STREAM,0);
if( fd <0)
{
perror("socket");
exit(1);
}
//把结构体清零
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(SERV_IP);//两个字节
/*
inet_addr(),返回转换后的地址,仅适用于IPV4,出错时返回-1
将IPV4/IPV6 的地址转换成binary格式
inet_pton(AF_INET,SERV_IP_ADDR ,(void*) sin.sin_addr.s_addr);
两种方法都可以
*/
sin.sin_addr.s_addr=inet_addr(SERV_IP_ADDR);
//优化,可以在任何的服务器中运行,自动获取当前的ip地址
//sin.sin_addr.s_addr=INADDR_ANY;
//2.bind
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
perror("bind");
exit(1);
}
//3.listen
if(listen(fd,BACKLOG)<0)
{
perror("listen");
exit(1);
}
//4.acccept
//进程
int newfd = -1;
pid_t pid;
struct sockaddr_in cin;
socklen_t addrlen =sizeof(cin);
while(1)
{
newfd=accept(fd,NULL,NULL);
if( newfd <0)
{
perror("acccept");
exit(1);
}
pid =fork();
if( pid <0)
{
perror("fork");
break;
}
if ( pid == 0)
{
char ipv4_addr[16];
if(! inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
{
perror("inet_ntop");
exit(1);
}
//获取端口号和ip地址
printf("client : (%s , %d) is connect !\n", ipv4_addr,ntohs(cin.sin_port));
client_data_handle(&newfd);
close(fd);
}
if( pid > 0)
{
close(newfd);
}
}
close(fd);
return 0;
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFSIZE 1024
#define QUIT_STR "QUIT"
#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.224.132"
int main(int argc , char **argv)
{
int fd = -1;
if( argc != 3)
{
exit(1);
}
int port = -1;
port=atoi(argv[2]);
struct sockaddr_in sin;
//1.socket
fd=socket(AF_INET,SOCK_STREAM,0);
if( fd <0)
{
perror("socket");
exit(1);
}
//把结构体清零
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(port);
sin.sin_addr.s_addr=inet_addr(argv[1]);
if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
{
perror("connect");
exit(1);
}
char buf[BUFSIZE];
while(1)
{
bzero(buf,BUFSIZE);
if(fgets(buf ,BUFSIZE-1,stdin) == NULL)
{
continue;
}
write(fd,buf,strlen(buf));
if( !strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
{
break;
}
}
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFSIZE 1024
#define QUIT_STR "QUIT"
#define SERV_IP 5001
#define SERV_IP_ADDR "192.168.224.132"
#define BACKLOG 5
void *client_data_handle( void *arg)
{
//强转为int型的指针,对它再取值
int newfd=*(int *) arg;
int ret = -1;
char buf[BUFSIZE];
printf(" pthread progess : newfd =%d\n",newfd);
while(1)
{
do
{
bzero(buf,BUFSIZE);
ret=read(newfd,buf,BUFSIZE-1);
}while(ret < 1);
if(ret < 0)
{
exit(1);
}
if ( ! ret)
{
break;
}
printf(" recevice data :%s\n",buf);
//两个字符串做比较
if( ! strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))) {
printf(" Client is exiting !\n");
break;
}
}
close(newfd);
return NULL;
}
void child_data_handle(int signum)
{
if( SIGCHLD == signum)
{
waitpid(-1,NULL,WNOHANG);
}
}
int main()
{
printf(" this is service!\n");
int fd = -1;
signal(SIGCHLD,child_data_handle);
struct sockaddr_in sin;
//1.socket
fd=socket(AF_INET,SOCK_STREAM,0);
if( fd <0)
{
perror("socket");
exit(1);
}
//把结构体清零
bzero(&sin,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(SERV_IP);//两个字节
sin.sin_addr.s_addr=inet_addr(SERV_IP_ADDR);
//优化,可以在任何的服务器中运行,自动获取当前的ip地址
//sin.sin_addr.s_addr=INADDR_ANY;
//2.bind
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
perror("bind");
exit(1);
}
//3.listen
if(listen(fd,BACKLOG)<0)
{
perror("listen");
exit(1);
}
//4.acccept
/*
int newfd = -1;
newfd=accept(fd,NULL,NULL);
if( newfd <0)
{
perror("acccept");
exit(1);
}
*/
//优化,服务端知道哪个客户端连接了,并可以获取端口号和ip地址
/*
每接收一个就打印它的客户信息,并创建一个线程,并跳转到线程这个函数里面去
*/
int newfd = -1;
pthread_t tid;
struct sockaddr_in cin;
socklen_t addrlen =sizeof(cin);
while(1)
{
newfd=accept(fd,(struct sockaddr *)&cin,&addrlen);
if( newfd <0)
{
perror("acccept");
exit(1);
}
char ipv4_addr[16];
if(! inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
{
perror("inet_ntop");
exit(1);
}
//获取端口号和ip地址
printf("client : (%s , %d) is connect !\n", ipv4_addr,ntohs(cin.sin_port));
pthread_create(&tid,NULL,client_data_handle,(void *)&newfd);
}
close(fd);
return 0;
}
服务端和TCP实现多进程并发服务器的服务端代码一致