1、新建两个程序,分别引用两个函数,先执行server端的程序,再执行client端的程序
2、实现功能:当client和sever连接成功后,从client输入什么都会传输给server端,当输入第一个字母为q时 两端程序都会退出
3、特别注意:需要修改SERVER_HOST 为自己主机地址
4、本程序编写的环境,如果时windows下执行可能需要修改头文件什么的,耐心一点看就好
gcc -v
gcc version 11.3.0 (Ubuntu 11.3.0-1ubuntu1~22.04.1)cmake -version
cmake version 3.22.1make -v
GNU Make 4.3
1、再任意主机上可以运行代码
2、添加读取客户信息
3、允许地址快速重用,当服务器断开后可以快速重用
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32
void *cli_data_handle(void *arg)
{
int newfd = *(int *)arg;
char buf[SERVER_SIZE];
printf("%s %d newfd = %d\n", __func__, __LINE__, newfd);
while (1)
{
memset(buf, 0, SERVER_SIZE);
int recread = read(newfd, buf, SERVER_SIZE - 1);
if (recread > 0)
{
printf("%s %d buf = %s\n", __func__, __LINE__, buf);
if (!strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT)))
{
printf("%s %d q======\n", __func__, __LINE__);
break;
}
}
usleep(100);
}
close(newfd);
return NULL;
}
int test_server()
{
printf("%s %d \n", __func__, __LINE__);
int fd = -1;
struct sockaddr_in addr_in;
char buf[SERVER_SIZE];
/*
AF_INET:IPV4
SOCK_STREAM:TCP
*/
// 1、创建socket 得到fd
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
printf("%s %d fd<0\n", __func__, __LINE__);
return 0;
}
// 优化3 允许地址快速重用,当服务器断开后可以快速重用
int b_reuse = 1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
// 2、绑定
memset(&addr_in, 0, sizeof(addr_in)); // 将变量addr_in置0
addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
addr_in.sin_family = AF_INET;
// int rec = inet_pton(AF_INET,SERVER_HOST,(void*)&addr_in.sin_addr.s_addr);
// if(rec!= 1){
// printf("%s %d rec!= 1\n", __func__, __LINE__);
// return 0;
// }
// addr_in.sin_addr.s_addr = inet_addr(SERVER_HOST); // 将主机从点分形式转换为32字节
// 优化1:
addr_in.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP可以运行
int rec = bind(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
if (rec < 0)
{
printf("%s %d rec<0\n", __func__, __LINE__);
return 0;
}
// 3、listen() 将主动套接字转换为被动套接字
int reclisten = listen(fd, SERVER_BACKLOG); // 允许正在进行连接的客户端数目
if (reclisten < 0)
{
printf("%s %d reclisten<0\n", __func__, __LINE__);
return 0;
}
// 4、阻塞等待客户端连接请求
#if 0
#if 0
int newfd = accept(fd, NULL, NULL);
if (newfd < 0)
{
printf("%s %d newfd<0\n", __func__, __LINE__);
return 0;
}
#else
// 优化2:
struct sockaddr_in addr_c;
socklen_t addr_len = sizeof(addr_c);
int newfd = accept(fd, (struct sockaddr *)&addr_c, &addr_len);
if (newfd < 0)
{
printf("%s %d newfd<0\n", __func__, __LINE__);
return 0;
}
char ipV4_addr[16];
if (!inet_ntop(AF_INET, (void *)&addr_c.sin_addr.s_addr, ipV4_addr, sizeof(addr_c)))
{
// 为空取反 不为空 就执行这里
printf("%s %d newfd<0\n", __func__, __LINE__);
return 0;
}
printf("%s %d addr_c.sin_port = %d\n", __func__, __LINE__, ntohs(addr_c.sin_port));
printf("%s %d addr_c.sin_addr = %s\n", __func__, __LINE__, ipV4_addr);
#endif
#else
pthread_t tid;
struct sockaddr_in addr_c;
socklen_t addr_len = sizeof(addr_c);
int newfd;
while (1)
{
newfd = accept(fd, (struct sockaddr *)&addr_c, &addr_len);
if (newfd < 0)
{
printf("%s %d newfd<0\n", __func__, __LINE__);
return 0;
}
char ipV4_addr[16];
if (!inet_ntop(AF_INET, (void *)&addr_c.sin_addr.s_addr, ipV4_addr, sizeof(addr_c)))
{
// 为空取反 不为空 就执行这里
printf("%s %d newfd<0\n", __func__, __LINE__);
return 0;
}
printf("%s %d addr_c.sin_port = %d\n", __func__, __LINE__, ntohs(addr_c.sin_port));
printf("%s %d addr_c.sin_addr = %s\n", __func__, __LINE__, ipV4_addr);
pthread_create(&tid, NULL, cli_data_handle, (void *)&newfd);
}
close(fd);
printf("%s %d xxxxxxxxxxxxxx newfd = %d\n", __func__, __LINE__, fd);
#endif
// 5、读写
// 和最新的newfd进行通信
return 0;
}
使用该函数时需要注意输入参数:
./project_cmake serv_ip ser_port
./project_cmake 192.168.0.43 5001 //服务器主机的IP和是设置的端口号
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32
void usage(char *s)
{
printf("%s %d %s serv_ip ser_port\n", __func__, __LINE__, s);
printf("%s %d serv_ip : server ip address\n", __func__, __LINE__);
printf("%s %d ser_port: server port (>5000)\n", __func__, __LINE__);
}
/*
./client serv_ip ser_port
*/
int test_client(int argc, char **argv)
{
printf("%s %d \n", __func__, __LINE__);
int port;
if (argc != 3)
{
usage(argv[0]);
return 0;
}
int fd = -1;
struct sockaddr_in addr_in;
char buf[SERVER_SIZE];
/*
AF_INET:IPV4
SOCK_STREAM:TCP
*/
// 1、创建socket 得到fd
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
printf("%s %d fd<0\n", __func__, __LINE__);
return 0;
}
port = atoi(argv[2]);
if (port < 5000)
{
usage(argv[0]);
return 0;
}
/*
2、连接
*/
memset(&addr_in, 0, sizeof(addr_in)); // 将变量addr_in置0
addr_in.sin_port = htons(port); // 将端口号从本地字节序转换为网络字节序
addr_in.sin_family = AF_INET;
int rec = inet_pton(AF_INET, argv[1], (void *)&addr_in.sin_addr.s_addr);
if (rec != 1)
{
printf("%s %d rec!= 1\n", __func__, __LINE__);
return 0;
}
int reccon = connect(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
if (reccon < 0)
{
printf("%s %d reccon<0\n", __func__, __LINE__);
return 0;
}
// 读写
while (1)
{
memset(buf, 0, SERVER_SIZE);
char *rec_p = fgets(buf, SERVER_SIZE - 1, stdin);
int recwrite = write(fd, buf, strlen(buf));
if (recwrite > 0)
{
printf("%s %d buf = %s\n", __func__, __LINE__, buf);
// int recstr = strcmp(buf, SERVER_QUIT);
int recstr = strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT));
printf("%s %d recstr = %d\n", __func__, __LINE__, recstr);
if (0 == recstr)
{
printf("%s %d q======\n", __func__, __LINE__);
break;
}
}
usleep(100);
}
close(fd);
printf("%s %d xxxxxxxxxxxxxx\n", __func__, __LINE__);
return 0;
}
下面的代码是上面的基础将进行进行修改的,可以参考着看!
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 5001 // 本地字节序
#define SERVER_HOST "192.168.0.43"
#define SERVER_BACKLOG 5
#define SERVER_QUIT "q"
#define SERVER_SIZE 32
void *cli_data_handle(void *arg)
{
int newfd = *(int *)arg;
char buf[SERVER_SIZE];
printf("%s %d newfd = %d\n", __func__, __LINE__, newfd);
while (1)
{
memset(buf, 0, SERVER_SIZE);
int recread = read(newfd, buf, SERVER_SIZE - 1);
if (recread > 0)
{
printf("%s %d buf = %s\n", __func__, __LINE__, buf);
if (!strncasecmp(buf, SERVER_QUIT, strlen(SERVER_QUIT)))
{
printf("%s %d q======\n", __func__, __LINE__);
break;
}
}
usleep(100);
}
close(newfd);
return NULL;
}
void sig_child_handle(int signo)
{
if (signo == SIGCHLD)
{
waitpid(-1, NULL, WNOHANG);
}
}
int test_server()
{
printf("%s %d \n", __func__, __LINE__);
int fd = -1;
struct sockaddr_in addr_in;
char buf[SERVER_SIZE];
signal(SIGCHLD, sig_child_handle);
/*
AF_INET:IPV4
SOCK_STREAM:TCP
*/
// 1、创建socket 得到fd
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
printf("%s %d fd<0\n", __func__, __LINE__);
return 0;
}
// 优化4 允许地址快速重用,当服务器断开后可以快速重用
int b_reuse = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
// 2、绑定
memset(&addr_in, 0, sizeof(addr_in)); // 将变量addr_in置0
addr_in.sin_port = htons(SERVER_PORT); // 将端口号从本地字节序转换为网络字节序
addr_in.sin_family = AF_INET;
// int rec = inet_pton(AF_INET,SERVER_HOST,(void*)&addr_in.sin_addr.s_addr);
// if(rec!= 1){
// printf("%s %d rec!= 1\n", __func__, __LINE__);
// return 0;
// }
// addr_in.sin_addr.s_addr = inet_addr(SERVER_HOST); // 将主机从点分形式转换为32字节
// 优化1:
addr_in.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP可以运行
int rec = bind(fd, (struct sockaddr *)&addr_in, sizeof(addr_in));
if (rec < 0)
{
printf("%s %d rec<0\n", __func__, __LINE__);
return 0;
}
// 3、listen() 将主动套接字转换为被动套接字
int reclisten = listen(fd, SERVER_BACKLOG); // 允许正在进行连接的客户端数目
if (reclisten < 0)
{
printf("%s %d reclisten<0\n", __func__, __LINE__);
return 0;
}
// 4、阻塞等待客户端连接请求
struct sockaddr_in addr_c;
socklen_t addr_len = sizeof(addr_c);
int newfd;
while (1)
{
pid_t pid = -1;
newfd = accept(fd, (struct sockaddr *)&addr_c, &addr_len);
if (newfd < 0)
{
printf("%s %d newfd<0\n", __func__, __LINE__);
break;
}
// 创建一个子进程用于处理已建立连接的客户的交互数据
if ((pid = fork()) < 0)
{
printf("%s %d pid = fork() < 0 \n", __func__, __LINE__);
break;
}
if (0 == pid)
{ // 子进程
close(fd);
char ipV4_addr[16];
if (!inet_ntop(AF_INET, (void *)&addr_c.sin_addr.s_addr, ipV4_addr, sizeof(addr_c)))
{
// 为空取反 不为空 就执行这里
printf("%s %d newfd<0\n", __func__, __LINE__);
return 0;
}
printf("%s %d addr_c.sin_port = %d\n", __func__, __LINE__, ntohs(addr_c.sin_port));
printf("%s %d addr_c.sin_addr = %s\n", __func__, __LINE__, ipV4_addr);
cli_data_handle(&newfd);
return 0;
}
else
{ // 父线程pid > 0
close(newfd);
}
}
close(fd);
printf("%s %d xxxxxxxxxxxxxx newfd = %d\n", __func__, __LINE__, fd);
// 5、读写
// 和最新的newfd进行通信
return 0;
}
和上面一致,这里不赘述。
使用该函数时需要注意输入参数:
./project_cmake serv_ip ser_port
./project_cmake 192.168.0.43 5001 //服务器主机的IP和是设置的端口号