目录
1、前言
2、函数介绍
2.1 socket函数 与 通信域
2.2 bind函数 与 通信结构体
2.2.1 domain通信地址族 与 通信结构体
2.2.2 IPv4地址族结构体
2.2.3 通用地址族结构体
2.2.4 示例:为套接字fd绑定通信结构体addr
2.3 listen函数 与 accept函数
3、代码实现
3.1 服务器端代码
3.2 客户端代码
3.3 构建Makefile
4、实验结果
1、前言
使用Linux操作系统实现TCP的客户端及服务器
TCP通信的实现过程示意图如下:
2、函数介绍
2.1 socket函数 与 通信域
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
- domain: 指定通信域(通信地址族),AF_INET: 使用IPv4 互联网协议;AF_INET6: 使用IPv6 互联网协议;
- type: 指定套接字类型,TCP唯一对应流式套接字,所以选择SOCK_STREAM(数据报套接字:SOCK_DGRAM);
- protocol: 指定协议,流式套接字唯一对应TCP,所以无需要指定协议,设为0即可。
2.2 bind函数 与 通信结构体
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- sockfd:socket函数生成的套接字
- addr:通信结构体
- addrlen:通信结构体的长度
2.2.1 domain通信地址族 与 通信结构体
2.2.2 IPv4地址族结构体
struct sockaddr_in {
sa_family_t sin_family; /* 地址族: AF_INET */
in_port_t sin_port; /* 网络字节序的端口号 */
struct in_addr sin_addr; /*IP地址结构体 */
};
/* IP地址结构体 */
struct in_addr {
uint32_t s_addr; /* 网络字节序的IP地址 */
};
2.2.3 通用地址族结构体
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
2.2.4 示例:为套接字fd绑定通信结构体addr
addr.sin_family = AF_INET;
addr.sin_port = htons(5001);
addr.sin_addr.s_addr = 0;//本机地址
bind(fd, (struct sockaddr *)&addr, sizeof(addr) );
2.3 listen函数 与 accept函数
/*监听套接字*/
int listen(int sockfd, int backlog);
/*处理客户端发起的连接,生成新的套接字*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- sockfd: 函数socket生成的套接字
- addr:客户端的地址族信息
- addrlen:地址族结构体的长度
3、代码实现
3.1 服务器端代码
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BACKLOG 5
int main(int argc,char *argv[])
{
int fd,newfd,ret;
char buf[BUFSIZ] = {};//BUFSIZ 8142
struct sockaddr_in addr;
if(argc < 3)
{
printf("%s<addr><port>\n",argv[0]);
exit(0);
}
/*创建套接字*/
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
exit(0);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
if(inet_aton(argv[1],&addr.sin_addr)==0)
{
fprintf(stderr,"Invalid address\n");
exit(0);
}
/*绑定通信结构体*/
if(bind(fd,(struct sockaddr *)&addr,sizeof(addr)) == -1)
{
perror("bind");
exit(0);
}
/*设置套接字为侦听模式*/
if(listen(fd,BACKLOG) == -1)
{
perror("listen");
exit(0);
}
/*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
newfd = accept(fd,NULL,NULL);
if(newfd < 0)
{
perror("accept");
exit(0);
}
printf("BUFSIZ = %d\n",BUFSIZ);
while(1)
{
memset(buf,0,BUFSIZ);
ret = read(newfd,buf,BUFSIZ);
if(ret < 0 )
{
perror("read");
exit(0);
}
else if(ret == 0)
break;
printf("buf = %s\n",buf);
}
close(newfd);
close(fd);
return 0;
}
3.2 客户端代码
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define BACKLOG 5
int main(int argc,char *argv[])
{
int fd;
struct sockaddr_in addr;
char buf[BUFSIZ] = {};
if(argc < 3)
{
printf("%s<addr><port>\n",argv[0]);
exit(0);
}
/*创建套接字*/
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
exit(0);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
if(inet_aton(argv[1],&addr.sin_addr)==0)
{
fprintf(stderr,"Invalid address\n");
exit(0);
}
/*向服务端发起连接请求*/
if(connect(fd,(struct sockaddr *)&addr,sizeof(addr)) == -1)
{
perror("connect");
exit(0);
}
while(1)
{
printf("Input->");
fgets(buf,BUFSIZ,stdin);
write(fd,buf,strlen(buf));
}
close(fd);
return 0;
}
3.3 构建Makefile
Makefile文件如下:
CC=gcc
CFLAGS=-Wall
all:client server
clean:
rm client server
使用make去构建服务器和客户端程序。
在服务器端传入ip地址和端口号,本机地址写0,端口号写5001。
./server 0 5001
在客户端传入ip地址和端口号,连接地址写127.0.0.1(本地回环地址),端口号写5001。
./client 127.0.0.1 5001
4、实验结果
通过截图可以看到,测试成功,客户端发送消息,服务器端可以接收并打印。