之前我们讲了用socket实现UDP网络程序,这篇博客我讲一下用socket实现TCP网络程序。
首先让我们来看下面的图:
服务端
1.程序的第一步是创建socket套接字
#include //头文件
//创建套接字函数,socket
int socket(int domain, int type, int protocol);
//domain:地址域,常用的有以下两种
// AF_INET IPv4 Internet protocols ip(7)
// AF_INET6 IPv6 Internet protocols ipv6(7)
//type:套接字类型,常用的有以下两种
// SOCK_STREAM 流式套接字
// SOCK_DGRAM 数据报套接字
//protocol:协议类型
// 默认为0,流式套接字默认TCP协议,数据包套接字默认UDP协议
// 在这里我们直接用UDP协议,即 IPPROTO_UDP
//返回值:int型,成功返回套接字描述符,失败返回-1
2.程序的第二步是为套接字绑定地址信息
#include //头文件
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// sockfd:套接字描述符
// addr:要绑定的地址信息
// addrlen:地址信息的长度
// 返回值:int型,成功返回0,失败返回-1
3.程序的第三步是监听,连接成功之后,获取新的socket连接
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
// backlog:最大同时并发连接数(同一时间)
4.程序的第四步是等待客户端连接请求到达
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// sockfd: socket描述符
// addr: 新建立连接的客户端地址信息
// addrlen:地址信息长度
// 返回值:返回新的socket连接描述符,失败:-1
new_sockfd 与 lst_sockfd 区别
lst_sockfd这是一个拉皮条的socket,说所有的连接请求的数据都是发送到这个socket的缓冲区,然后进行处理,会为这个新连接的客户端新建一个socket。 (这个socket接收到的数据都是连接请求)
new_sockfd这是一个干活的socket,连接建立成功之后,这个socket有自己的缓冲区往后这个客户端所发送的数据都是在这个socket的缓冲区中。
accept函数是一个阻塞型的函数,连接成功队列中如果没有新的连接,那么就会一直阻塞直到有新的客户端连接到来
5.程序的第五步是接收数据
#include //头文件
#include
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen)
// sockfd:套接字描述符
// buf:用于存储接受的数据,把数据拷贝到buf当中
// len:想要接受的数据长度
// flags:默认为0,如果缓冲区没有数据,就阻塞等待直到有数据并拷贝完毕
// src_addr:用于确定数据是哪一个客户端发送的,即确定发送端的地址信息
// addrlen:发送端地址信息的长度,不能为0
// 返回值:ssize_t类型, 成功返回实际接收数据的长度,失败返回-1
6.程序的第六步是发送数据
#include<sys/types.h> //头文件
#include<sys/socket.h>
ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr *dest_addr,socklen_t addrlen);
// sockfd:socket描述符,发送数据的时候就是通过这个socket所绑定的地址来发送
// buf:要发送的数据
// len: 要发送数据的长度
// flags:0-默认阻塞式发送
// dest_addr:数据要发送的目的地址,对端地址
// addrlen:地址信息长度
// 返回值:ssize_t类型,返回实际发送的数据长度,失败返回-1
7.程序的第五步是关闭socket,即close(sockfd)
客户端
客户端与服务器端不同的是在创建套接字之后,会连接服务器端,这里就有一个connect函数。
int connect(int sockfd, struct sockaddr *addr,socklen_t addrlen);
// sockfd: socket描述符
// addr: 要连接的服务端地址
// addrlen: 地址信息长度
// 返回值:成功:0 失败:-1
下面是源代码:
tcp_client.c
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0)
{
perror("socket error");
return -1;
}
//2.绑定:客户端程序不推荐绑定地址
//3.向服务端发起连接请求
struct sockaddr_in serv_addr;
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(9000);
serv_addr.sin_addr.s_addr=inet_addr("192.168.1.101");
//#include <sys/types.h> /* See NOTES */
//#include <sys/socket.h>
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// sockfd:套接字描述符
// addr:要连接的服务器地址
// addrlen:地址信息长度
// 返回值:成功返回0,失败返回-1
socklen_t len=sizeof(struct sockaddr_in);
int ret=connect(sockfd,(struct sockaddr*)&serv_addr,len);
if(ret<0)
{
perror("connect error");
return -1;
}
while(1)
{
//4.发送数据
char buff[1024]={0};
scanf("%s",buff);
send(sockfd,buff,strlen(buff),0);
//5.接收数据
memset(buff,0x00,1024);
ssize_t rlen = recv(sockfd, buff, 1023, 0);
if (rlen < 0)
{
perror("recv error");
return -1;
}
else if (rlen == 0)
{
printf("peer shutdown!\n");
return -1;
}
printf("server say:%s\n", buff);
}
close(sockfd);
return 0;
}
tcp_server.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
//1.创建套接字socket
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0)
{
perror("socket error");
return -1;
}
//2.为socket绑定地址信息
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(9000);
addr.sin_addr.s_addr=inet_addr("192.168.1.101");
socklen_t len=sizeof(struct sockaddr_in);
int ret=bind(sockfd,(struct sockaddr *)&addr,len);
if(ret<0)
{
perror("bind error");
return -1;
}
//3. 监听
// int listen(int sockfd, int backlog);
// sockfd: socket描述符
// backlog: 最大的同时并发连接数
if(listen(sockfd,5)<0)
{
perror("listen error");
return -1;
}
//4.获取连接成功的socket
while(1)
{
int new_sockfd;
struct sockaddr_in cli_addr;
len=sizeof(struct sockaddr_in);
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// sockfd: socket描述符
// addr: 新建立连接的客户端地址信息
// addrlen:地址信息长度
// 返回值:返回新的socket连接描述符,失败:-1
new_sockfd=accept(sockfd,(struct sockaddr *)&cli_addr,&len);
if(new_sockfd<0)
{
perror("accept error");
continue;
}
printf("new conn %s:%d\n", inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));
while(1)
{
//5.接收数据
char buff[1024]={0};
ssize_t rlen=recv(new_sockfd,buff,1023,0);
if(rlen<0)
{
perror("recv error");
close(new_sockfd);
continue;
}else if(rlen ==0)
{
perror("peer shutdown\n");
close(new_sockfd);
continue;
}
printf("client[%s:%d] say:%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port), buff);
//6.发送数据
memset(buff, 0x00, 1024);
scanf("%s", buff);
send(new_sockfd, buff, strlen(buff), 0);
}
}
close(sockfd);
return 0;
}