TCP 协议是一种面向连接的、可靠的字节流服务。
TCP协议的编程流程
服务器端
socket();//创建一个用于监听客户端连接的网络套接字
bind();//将创建的套接字与本段的地址信息进行绑定
listen();//启动监听,不会阻塞
accept();//接收一个客户端的连接,返回的是一个客户端连接套接字
recv(); /send(); //读取数据或发送数据
close();//关闭文件描述符(套接字)
客户端
socket(); //创建一个用于整个通讯的套接字
connect(); //与服务器建立连接
recv(); /send(); //读取或发送数据
close(); //关闭文件描述符
所涉及到的系统调用用法
int socket(int domain, int type, int protocol);//成功返回 0 ,失败返回-1
domain:协议族 AF_INET
type:具体的协议 SOCK_STREAM (TCP) SOCK_DGRAM (UDP)
protocol:0 默认
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//成功返回0,失败返回 -1
sockfd:使用socket方法创建的套接字描述符
addr:服务器程序的地址信息 IP+端口号
addr_len:指定结构体大小
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
一般使用sockaddr_in ,用指针类型强转成sockaddr类型的
struct sockaddr_in
{
sa_family_t sin_family; //地址族 AF_INET
unsigned short sin_port; //端口号
struct in_addr sin_addr; // IP地址信息 点分十进制字符串 转换成u_int32_t
};
struct in_addr
{
u_int32_t s_addr;
};
int listen(int sockfd, int backlog);//成功返回0 ,失败返回 -1
sockfd:创建并绑定过的套接字描述符
backlog:指定内核创建的用于维护当前客户端连接(还没被accept)的
队列(已完成连接)的大小
(有两个队列:正在进行连接的 和 已完成连接的)
int accept(int sockfd, struct sockaddr *addr,socklen_t *addrlen);//成功返回与一个客户端连接的文件描述符,失败返回 -1
sockfd:经过listen的监听套接字
addr:用于保存接收到的客户端的地址信息
addrlen:地址结构的长度
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//与服务器进程建立连接 *addr是服务器的地址信息
举例
在linux下,编写服务器端和客户端代码,服务器端接收客户端的连接,并打印客户端发送过来的数据,代码如下:
/*服务器端*/
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
int listenfd=socket(AF_INET,SOCK_STREAM,0);
assert(listenfd!=-1);
struct sockaddr_in ser_addr;
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family=AF_INET;
ser_addr.sin_port=htons(6000);
ser_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//回环地址
int res=bind(listenfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
assert(res!=-1);
res=listen(listenfd,5);
assert(res!=-1);
while(1)
{
struct sockaddr_in cli_addr;
socklen_t len=sizeof(cli_addr);
int clientLinkFd=accept(listenfd,(struct sockaddr*)&cli_addr,&len);
if(clientLinkFd==-1)
{
printf("one client error\n");
continue;
}
printf("onr client success:--%s:%d\n",inet_ntoa(cli_addr.sin_addr),n tohs(cli_addr.sin_port));
while(1)
{
char buff[128]={0};
int num=recv(clientLinkFd,buff,127,0);//0--默认方式
if(num==-1)
{
printf("recv error\n");
break;
}
else if(num ==0)
{
printf("client over\n");
break;
}
printf("recv data is : %s\n",buff);
char *restr="recv data success";
num=send(clientLinkFd,restr,strlen(restr),0);
if(num==-1)
{
printf("send data error\n");
break;
}
}
close(clientLinkFd);
}
close(listenfd);
exit(0);
}
/*客户端*/
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
struct sockaddr_in ser_addr;
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family=AF_INET;
ser_addr.sin_port=htons(6000);
ser_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//回环地址
int res=connect(sockfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
assert(res!=-1);
while(1)
{
char buff[128]={0};
printf("input: ");
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
send(sockfd,buff,strlen(buff),0);
memset(buff,0,128);
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
}
close(sockfd);
exit(0);
}
运行结果如下(注意先启动服务器端):