基本TCP客户/服务器程序的套接字函数
socket函数
头文件:<sys/socket.h>
功能:创建一个套接字用于通信
原型:int socket(int family,int type,int protocol);
参数:
family:指定通信协议族
type:指定套接字类型,流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW。
protocol:协议类型
返回值:成功返回非负整数,它与文件描述符类似,称为套接字描述符,简称sockfd。
bind函数
头文件:<sys/socket.h>
功能:绑定一个本地地址到套接字
原型:int bbind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
参数:
sockfd:socket函数返回的套接字
addr:要绑定的地址
addrlen:地址长度
返回值:成功返回0,失败返回-1
listen函数
头文件:<sys/socket.h>
功能:将套接字用于监听进入的连接
原型:int listen(int sockfd,int backlog);
参数:
sockfd:socket函数返回的套接字
backlog:规定内核为此套接字排队的最大连接个数。
返回值:成功返回0,失败返回-1
说明:
listen函数仅由TCP服务器调用,它做两件事情。
当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字。listen函数把一个未连接的套接字转换为一个被动套接字,指示内核应该接受指向该套接字的连接请求。调用listen导致套接字从CLOSED状态转换到LISTEN状态。
listen函数的第二个参数规定了内核应该为相应套接字排队的最大连接个数。
当进程调用accept时,已完成队列的队头将返回给进程,或者如果该队列为空,那么进程将被投入睡眠,直到TCP在该队列中放入一项才唤醒它。
accept函数
头文件:<sys/socket.h>
功能:从已完成队列返回第一个连接,如果已完成连接队列为空,则阻塞。
原型:int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
参数:
sockfd:服务器套接字
addr:将返回对等方的套接字
addrlen:返回对等方的套接字地址长度
返回值:成功返回非负整数,失败返回-1。
说明:
如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(也就是说对于它的TCP三路握手过程已经完成,这是一个主动套接字。当服务器完成对某个给定客户的服务时,相应的已连接套接字就被关闭。
connect函数
头文件:<sys/socket.h>
功能:TCP客户用connect函数来建立与TCP服务器的连接。
原型:int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen);
参数:
sockfd:socket函数返回的套接字
第二个、第三个参数分别是一个指向套接字地址结构的指针和该结构体的大小
返回值:若成功则返回0,失败则为-1。
说明:
客户在调用函数connect前不必非得调用bind函数,因为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口。
如果是TCP套接字调用connect函数将激发TCP的三路握手过程,而且仅在连接建立成功或出错时才返回。
TCP客户端/服务端实现代码
服务端
#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0) int main(void) { //创建一个套接字,相当于安装一部话机 int listenfd; //实际上前两个参数已经决定是TCP协议,最后一个参数可以写0 if((listenfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) /*if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0) */ ERR_EXIT("socket"); //IVp4的地址结构 struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family=AF_INET;//地址族 servaddr.sin_port=htons(5188);//端口号 //INADDR_ANY表示本机的任意地址 servaddr.sin_addr.s_addr=htonl(INADDR_ANY); //显示指定某个地址 /*servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");*/ /*inet_aton("127.0.0.1,&servaddr.sin_addr");*/ int on=1; if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0) ERR_EXIT("setsockopt"); //绑定的是一个通用地址,需要进行转化 if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0) ERR_EXIT("bind"); //SOMAXCONN这个宏表示队列的最大值 //一旦调用之后,套接字就由主动套接字变为被动套接字,主动套接字用来发起连接(调用conn),被动套接字用来接收连接(调用accept) if(listen(listenfd,SOMAXCONN)<0) ERR_EXIT("listen"); //对方的地址 struct sockaddr_in peeraddr; //peerlen一定要有初始值,否则accept失败 socklen_t peerlen=sizeof(peeraddr); int conn; //从完成连接队列的队列得到一个连接 //conn是已连接套接字,是主动套接字了 if((conn=accept(listenfd,(struct sockaddr*)&peeraddr,&peerlen))<0) ERR_EXIT("accept"); printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port)); char recvbuf[1024]; while(1) { memset(recvbuf,0,sizeof(recvbuf)); int ret=read(conn,recvbuf,sizeof(recvbuf)); fputs(recvbuf,stdout); write(conn,recvbuf,ret); } close(conn); close(listenfd); return 0; }
客户端
#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ }while(0) int main(void) { int sock; if((sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0) /*if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0) */ ERR_EXIT("socket"); struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_port=htons(5188); //服务器端地址 servaddr.sin_addr.s_addr=inet_addr("127.0.0.1"); /*inet_aton("127.0.0.1,&servaddr.sin_addr");*/ if(connect(sock,(struct sockaddr*)&servaddr,sizeof(servaddr))<0) ERR_EXIT("connect"); char sendbuf[1024]={0}; char recvbuf[1024]={0}; while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL) { write(sock,sendbuf,strlen(sendbuf)); read(sock,recvbuf,sizeof(recvbuf)); fputs(recvbuf,stdout); //完成一次之后清空缓冲区 memset(&sendbuf,0,sizeof(sendbuf)); memset(&recvbuf,0,sizeof(recvbuf)); } //关闭套接口 close(sock); return 0; }