数据报套接字使用UDP,流式套接字使用TCP。UDP不连接但快速,TCP连接、安全。UDP不区分服务区段与客户端,分为发送端和接收端,TCP分为服务区段和客户端。
一.服务器端
服务器端操作流程:socket() --> bind() --> listen() -->accept() --> read() --> write() --> close()
1.socket()
int socket(int domain,int type,int protocol);失败返回-1
(1)domain地址族,套接字的域名:
AF_INET,PF_INET:IPv4;socket时使用PF_INET,bind时使用AF_INET
AF_INET6:IPv6.
(2)type类型
SOCK_STREAM流式套接字
SOCK_DGRAM数据报套接字
(3)protocol协议:一般为0
2.bind()
创建套接字后,需要和协议地址绑定
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addlen);失败返回-1,成功返回0
(1)sockfd:socket()返回结果
(2)myaddr:协议地址
struct sockaddr*为通用的套接字地址
例如,使用IPv4的套接字地址,在使用前会强制转换成struct sockaddr*类型
struct sockaddr_in server_addr; // IPv4的套接字地址
bind(sockfd,(struct sockaddr *)(&server_addr),siezof server_addr)) // 强转成通用套接字地址
struct sockaddr{
//地址族,IPv4为AF_INET
sa_family_t sa_family;
//地址数据
char sa_data[4];
}
IPv4套接字地址,最常用的套接字地址类型struct sockaddr_in,ip地址使用前要使用把点分式转换成大整数形。
inet_pton(AF_INET,"0.0.0.0",&server_addr.sin_addr); // inet_pton将点分式转化成大整数形,0.0.0.0可以替换本机地址,INADDR_ANY也可以指代本机地址。
server_addr.sin_addr.s_addr=htonl(INADDR_ANY); // 也可以把网络字节转换成32为大整数
端口号首先要从字符串转换成整数,再转换成18位short整数
server_addr.sin_port = htons(atoi(argv[1])) // atoi把字符串转换成整数
struct sockaddr_in{
//地址族:AF_INET
sa_family_t sin_family;
//端口
uint16_t sin_port;
//ip地址
struct in_addr sin_addr;
}
struct in_addr{
//ip地址,大整数形
uint32_t s_addr;
}
(3)addlen:协议地址的长度
3.listen()
监听函数,监听来自客户端的信息
int listen(int sockfd,int backlog)
backlog:默认为128
4.accept()
接收请求
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
(1)struct sockaddr *cliaddr 通用的协议地址,仍然需要强转,不过是客户端的协议地址
(2)socklen_t *addrlen:socklen_t是一种特殊数据类型,需要取地址作为参数,addrlen一般为struct sockaddr_in的长度,例:
struct sockaddr_in client_addr; // 客户端的协议地址
socklen_t size = sizeof(struct sockaddr_in)
accept(sockfd,(struct sockaddr *)(&client_addr),&size);
accept成功会返回一个新的套接字描述符,成为连接套接字,连接套接字用于和已经建立连接的客户端进行通信,原有的套接字用于继续监听后续新客户端发来的连接请求。失败返回-1
5.read()
读操作
ssize_t read(new_sockfd,void *buf,size_t count);
(1)buf:存放读入数据的缓存,char reqBuf[512]
(2)count:buf指向的输出缓存的大小,sizeof(reqBuf)
例:int z = read(new_sockfd,reqBuf,sizeof(reqBuf));
reqBuf[z]=0;
printf("%s",reqBuf);
成功返回所读字节数,0表示文件尾,-1表示失败
6.write()
写操作
ssize_t write(new_sockfd,const void *buf,size_t count);
例:fgets(reqBuf,sizeof(reqBuf),stdin);
z = write(new_sockfd,reqBuf,sizeof(reqBuf));
成功返回所写字节数,失败返回-1,0表示为写入任何信息
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define BUFSIZE 512
int main(int argc,char *argv[])
{
int sockfd;//服务器套接字
int new_sockfd;//连接套接字
struct sockaddr_in server_addr;//服务器地址
struct sockaddr_in client_addr;//客户端地址
socklen_t size;
int portnumber;//端口号
int flag=0;
char reqBuf[BUFSIZE];//接收缓存区
int z;//read()返回值
if(argc!=2)
{
fprintf(stderr,"%s portnumber\n",argv[0]);
exit(1);
}
if((portnumber=atoi(argv[1]))<0)
{
fprintf(stderr,"%s portnumber\n",argv[0]);
exit(1);
}
if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"socket error:%s\n",strerror(errno));
exit(1);
}
memset(&server_addr,0,sizeof server_addr);
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
if((bind(sockfd,(struct sockaddr *)(&server_addr),sizeof server_addr))==-1)
{
fprintf(stderr,"bind error:%s\n",strerror(errno));
exit(1);
}
if(listen(sockfd,128)==-1){
fprintf(stderr,"listen error:%s\n",strerror(errno));
exit(1);
}
printf("wait for the request from client\n");
while(1)
{
size=sizeof(struct sockaddr_in);
if((new_sockfd = accept(sockfd,(struct sockaddr *)(&client_addr),&size))==-1)
fprintf(stderr,"accept error:%s\n",strerror(errno));
fprintf(stdout,"server got connection from %s\n",inet_ntoa(client_addr.sin_addr));
while(1)
{
z=read(new_sockfd,reqBuf,sizeof reqBuf);
if(z<0)
{
fprintf(stderr,"read error:%s\n",strerror(errno));
exit(1);
}
if(z==0)
{
printf("close new_sockfd\n");
close(new_sockfd);
break;
}
reqBuf[z]=0;
printf("receive from client: %s\n",reqBuf);
if(!strncmp(reqBuf,"QUIT",sizeof reqBuf))
{
flag=1;
break;
}
printf("write down your message to client\n");
fgets(reqBuf,sizeof reqBuf,stdin);
z=write(new_sockfd,reqBuf,sizeof reqBuf);
if(z<0){
printf("write error");
exit(1);
}else{
printf("wait message from client\n");
}
}
if(flag==1)
{
printf("the server is ready to close,please enter any ket to exit\n");
getchar();
break;
}
}
}
二.客户端
socket() --> connect() --> write() --> read() --> close()
1.connect()
连接请求,与服务器端取得连接
int connect(int sockfd,const struct sockaddr* servaddr,socklen_t addrlen); //仍然需要强转
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define BUFSIZE 512
int main(int argc,char *argv[])
{
int sockfd;
char buf[BUFSIZE];
struct sockaddr_in server_addr;
struct hostent *host;
int portnumber;
int nbytes;
int z;
char reqBuf[BUFSIZE];
host=gethostbyname(argv[1]);
portnumber=atoi(argv[2]);
if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"socket error:%s\n",strerror(errno));
exit(1);
}
memset(&server_addr,0,sizeof server_addr);
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr*)host->h_addr);
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof server_addr)==-1)
{
fprintf(stderr,"connect error:%s\n",strerror(errno));
exit(1);
}
printf("connect success\n");
while(1)
{
printf("please input your message to server\n");
if(!fgets(reqBuf,sizeof reqBuf,stdin))
{
printf("fgets error\n");
break;
}
z=strlen(reqBuf);
if(z>0&&reqBuf[--z]=='\n')
reqBuf[z]=0;
if(z==0)
continue;
if(!strncmp(reqBuf,"QUIT",sizeof reqBuf))
{
printf("the Client closed.please enter any key to exit\n");
getchar();
write(sockfd,reqBuf,strlen(reqBuf));
break;
}
z=write(sockfd,reqBuf,strlen(reqBuf));
if(z<0)
{
fprintf(stderr,"write error:%s\n",strerror(errno));
exit(1);
}else{
printf("wait message from server\n");
}
if((z=read(sockfd,reqBuf,sizeof(reqBuf)))==-1)
{
fprintf(stderr,"read error:%s\n",strerror(errno));
exit(1);
}
if(z==0)
{
printf("the server closed.please enter any key to exit\n");
getchar();
break;
}
reqBuf[z]=0;
printf("receive message:%s\n",reqBuf);
}
close(sockfd);
return 0;
}
三.运行
先运行服务器端:./server 9000
后运行客户端:./client 0.0.0.0 9000
本地回环测试