- TCP是一种面向连接的、可靠的、基于字节流的传输协议。
- TCP具有以下特点:
- 每一次完整的数据传输都要经过建立连接、使用连接、终止连接的过程
- 可靠、出错传输、且没收到一个数据都要给出相应的确认,保证数据传输的可靠性
TCP的网络编程开发分为服务器端和客户端两部分,常见的步骤如下:
注意:通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来连接服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合,这就是为什么服务器在listen之前会调用bind(),而客户端就不需要调用,而是在connect()时由系统随机生成一个
TCP客户端编程(几个函数的介绍):
所需头文件:
#include<sys/socket.h>
1.函数:
int socket(int family,int type,int protocol);//创建一个用于网路通信的socket套接字(描述符)
2.参数:
- family:这里用AF_INET,代表IPv4
- type:这里用SOCK_STREAM,代表TCP数据流
- protocol:这里写0,设置0表示使用默认协议
3.返回值:
- 成功:套接字
- 失败:<0的值
1.函数:
int connect(int sockfd,const struct sockaddr* addr,socklen_t len);//主动跟服务器建立连接
2.参数:
- sockfd:sock()函数返回的套接字
- addr:连接的服务器地址结构
- len:地址结构体长度
3.返回值:
- 成功:0
- 失败:-1
1.函数:
size_t send(int sockfd,const void* buf,size_t nbytes,int flag);//发送数据,最后一个参数为0时,可以用write()替代。不能用TCP协议发送0长度的数据包,数据没有发送成功,内核会自动重发。
2.参数:
- sockfd:已建立连接的套接字
- buf:发送数据的地址
- nbytes:发送数据的大小(单位为字节)
- flags:套接字标志(常为0)
3.返回值:
- 成功:成功发送的字节数
- 失败:<0的值
对于客户端,也可以接收数据,前提是客户端先给服务端发送数据。
1.函数:
ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags);//接收网络数据,在默认的情况下,如果没有接收到数据,函数会阻塞,直到有数据接收
2.参数:
- sockfd:套接字
- buf:接受网络数据的缓冲区的地址
- nbytes:接收缓冲区的大小(单位为字节)
- flags:套接字标志(常为0)
3.返回值:
- 成功:接收的字节数
- 失败:<0的值
TCP服务器编程(函数的介绍):
作为TCP服务器需要具备的条件
- 具备一个可以确定的地址用来bind();
- 让操作系统知道这是一个服务器,而不是客户端,状态(listen);
- 等待连接的到来(accept);
接收端使用bind()函数,来完成地址结构与socket套接字的绑定。
1.函数:
int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen);//将本地协议地址与sockfd绑定,这样ip、port就固定了
2.参数:
- sockfd:socket套接字
- myaddr:指向特定协议的地址结构指针
- addrlen:该地址结构的长度
3.返回值:
- 成功:返回0
- 失败:-1
1.函数:
int listen(int sockfd,int backlog);//将套接字由主动修改为被动,使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的链接。
2.参数:
- sockfd:socket套接字
- backlog:连接队列的长度
3.返回值:
- 成功:返回0
- 失败:其他
1.函数:
int accept(int sockfd,struct sockaddr* cliaddr,socklen_t* addrlen);//从已连接队列取出一个已经建立的连接,如果没有任何连接可用,则进入谁灭等待(阻塞)
2.参数:
- sockfd:socket套接字
- cliaddr:用于存放客户端套接字地址结构
- addrlen:套接字地址结构体长度的地址
3.返回值:
- 成功:已连接套接字
- 失败:<0的值
关闭连接:close()
使用close()函数即可关闭套接字,关闭一个代表已连接套接字将导致另一端收到一个0长度的数据包。
简单实践:
server.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
int startup(int _port,const char* _ip)
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("socket error");
exit(1);
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = inet_addr(_ip);
int err_log = bind(sockfd,(struct sockaddr*)&local,sizeof(sockaddr_in));
if(err_log < 0)
{
perror("bind");
exit(2);
}
if(listen(sockfd,5) < 0)
{
perror("listen");
exit(3);
}
return sockfd;
}
int main(int argc,char** argv)
{
if(argc != 3)
{
printf("Usage:%s[local_ip][local_port]\n",argv[0]);
return 1;
}
int listen_sockfd = startup(atoi(argv[2]),argv[1]);
struct sockaddr_in remote;
socklen_t len = sizeof(struct sockaddr_in);
while(1)
{
int sockfd = accept(listen_sockfd,(struct sockaddr*)&remote,&len);
if(sockfd < 0)
{
perror("accept");
continue;
}
printf("get a client,ip:%s,port:%d\n",inet_ntoa(remote.sin_addr),htons(remote.sin_port));
char buf[512];
while(1)
{
ssize_t _s = read(sockfd,buf,sizeof(buf)-1);
if(_s > 0)
{
buf[_s] = 0;
printf("client:%s\n",buf);
}
else
{
printf("client is quit!\n");
break;
}
}
}
return 0;
}
client.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc,char** argv)
{
if(argc != 3)//更改服务器的ip地址
{
printf("Usage:%s [ip][port]\n",argv[0]);
return 0;
}
int sockfd;
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
perror("socket error");
return -1;
}
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET; //IPv4
server_addr.sin_port = htons(atoi(argv[2]));//端口
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
//inet_pton(AF_INET,server_ip,&server_addr.sin_addr);
int err_log = connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(err_log != 0)
{
perror("connect error");
close(sockfd);
exit(-1);
}
char buf[512];
while(1)
{
printf("send###");
fflush(stdout);
ssize_t _s = read(0,buf,sizeof(buf)-1);
buf[_s] = 0;
write(sockfd,buf,_s);
}
close(sockfd);
return 0;
}