一、题目
编写一个简单的服务器、客户端(使用TCP)——服务器一直监听本机的6666号端口,如果收到连接请求,将接收请求并接收客户端发来的消息;客户端与服务器端建立连接并发送一条消息。
二、知识准备
- Linux常用命令(vi/vim/gedit,gcc)等;
- 套接字编程基础知识
- TCP/IP网络编程
三、硬件准备
- VMware Workstation
- CentOs 7镜像以及安装系统
四、套接字知识
1.套接字是网络通信的基本操作单元,提供了不同主机之间进程双向通信的端点。
2.套接字分类:
- 字节流套接字(流式套接字),基于TCP,面向连接,可靠;
- 数据报套接字,基于UDP,无连接,不可靠,通常用于单个报文传输或可靠性不重要的场合。
- 原始套接字,超级用户使用,用于开发新的协议或提取协议比较隐蔽的功能。
3.套接字地址结构:
3.1.通用的地址结构体
struct sockaddr
{
unsigned short sa_family; //地址家族或协议簇,一般是“AF_xxx”形式
char sa_data[14]; //14字节协议地址,里面包含IP地址和端口
}
3.2.地址结构类型的名字为开始,以协议簇为后缀的地址结构体
以INET协议簇为例:
struct sockaddr_in
{
short int sin_family; //地址家族或协议簇,一般是“AF_INET”形式
unisigned short int sin_port; //存储端口号,需要注意网络字节顺序
struct in_addr sin_addr; //存储IP地址
unsigned char sin_zero[8]; //为了使sockaddr_in和sockaddr_in两个地址结构保持大小相同而保留的空字节,协议簇、端口号、IP地址共占用8字节,而sockaddr通用地址结构共16字节,所以sin_zero数组元素个数为8,使用时,将其清零。
}
4.面向连接套接字通信过程
4.1服务端
- 服务器进程首先调用socket创建一个流式套接字
- 调用bind将服务器所在的主机地址和端口号绑定在套接字上。
- 调用listen将套接字转换为被动监听套接字,并在套接字指定的端口上监听客户端连接请求。
- 调用accept做好与客户端进程建立连接的准备,如果没有客户端连接请求,那么服务器将被阻塞,知道客户端连接为止
- 当接收客户端的请求后,服务进程被唤醒,生成一个新的套接字,并相互建立连接,使用send(write)和recv(read)来发送和接收数据。
4.2客户端
- 客户端调用socket创建流式套接字
- 根据服务器的地址和端口,调用connect向服务进程发出连接请求
- 服务进程同意后,客户端通过send、recv与服务器进行数据通信
- 通信结束后,客户端关闭套接字,中断连接
五.代码
创建一个目录socket:mkdir /socket
创建一个服务器的代码文件:vi server.c
创建一个客户端的代码文件:vi client.c
1.服务器代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
int fd;
int ret;
int i;
struct sockaddr_in serv_addr;
//创建socket,指定IPv4协议族 TCP协议
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
perror("socket create error...");
exit(1);
}
//初始化一个地址
serv_addr.sin_family = AF_INET; //选择协议族为IPv4
serv_addr.sin_port = htons(6666); //绑定端口号
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址
//绑定服务器地址
printf("bind...\n");
ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (ret == -1)//bind failed!
{
perror("bind error\n");
}
printf("listen...\n");//设置监听上限,设定链接上限
ret = listen(fd, 128);
if (ret == -1)
{
perror("listen error\n");
}
printf("wait....\n");
//等待并接收连接请求
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
printf("accept...\n");
//监听客户端链接, 会阻塞
int cfd = accept(fd, (struct sockaddr *)&client_addr, &len);
if (cfd == -1)
{
perror("accept error");
}
printf("accept successful\n");
char ipbuf[64] = { 0 };
client_addr.sin_port = 6666;
printf("client ip:%s, port:%d\n",
inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
ntohs(serv_addr.sin_port));
send(cfd,"hello,you are connected!\n",26,0);
while (1)
{
char buf[1024] = { 0 };
int len = read(cfd, buf, sizeof(buf));
if (len == -1)
{
perror("read error");
}
else if (len == 0)
{
printf("客户端断开连接\n");
close(cfd);
break;
}
else
{
printf("receive from client:%s\n",buf);
printf("send(return) to client : %s", buf);
send(cfd,buf,100,0);
}
}
close(fd);
return 0;
}
2.客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <fcntl.h>
#define MAXDATASIZE 100
int main(int argc, char **argv)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
char buf[512];
if (fd == -1)
{
perror("socket error");
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(6666);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);
//与服务器建立连接
printf("create bind to service...\n");
int ret = connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret == -1)
{
perror("connect error");
}
//接收服务器发过来的bind successful !
int len = recv(fd,buf,MAXDATASIZE,0);
printf("%s\n",buf);
while (1)
{
fgets(buf, sizeof(buf), stdin);
//send 数据给服务器
send(fd,buf,MAXDATASIZE,0);
//接收服务器发过来的数据
int len = recv(fd,buf,MAXDATASIZE,0);
if (len == -1)
{
perror("read error");
}
else if (len == 0)
{
printf("服务器端关闭了连接\n");
break;
}
else
{
printf("receive from service : %s\n", buf);
}
}
close(fd);
return 0;
}
代码写好后,如下操作:
开启两个终端,一个模拟服务器,一个模拟客户端
分别执行一下命令:
gcc service.c -o service
./service
————————————————————
gcc client.c -o client
./client