使用方法
1、新建兩個程序,分別引用兩個函數(shù),先執(zhí)行server端的程序,再執(zhí)行client端的程序
2、實現(xiàn)功能:當client和sever連接成功后,從client輸入什么都會傳輸給server端,當輸入第一個字母為q時 兩端程序都會退出
3、特別注意:需要修改SERVER_HOST 為自己主機地址
4、本程序編寫的環(huán)境,如果時windows下執(zhí)行可能需要修改頭文件什么的,耐心一點看就好
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
一、使用線程實現(xiàn)并發(fā)服務(wù)器
1、服務(wù)器端代碼
優(yōu)化功能:
1、再任意主機上可以運行代碼
2、添加讀取客戶信息
3、允許地址快速重用,當服務(wù)器斷開后可以快速重用
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define SERVER_PORT 5001 // 本地字節(jié)序
#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、創(chuàng)建socket 得到fd
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
printf("%s %d fd<0\n", __func__, __LINE__);
return 0;
}
// 優(yōu)化3 允許地址快速重用,當服務(wù)器斷開后可以快速重用
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); // 將端口號從本地字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
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); // 將主機從點分形式轉(zhuǎn)換為32字節(jié)
// 優(yōu)化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() 將主動套接字轉(zhuǎn)換為被動套接字
int reclisten = listen(fd, SERVER_BACKLOG); // 允許正在進行連接的客戶端數(shù)目
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
// 優(yōu)化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)))
{
// 為空取反 不為空 就執(zhí)行這里
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)))
{
// 為空取反 不為空 就執(zhí)行這里
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;
}
2、客戶端代碼
注意:
使用該函數(shù)時需要注意輸入?yún)?shù):
./project_cmake serv_ip ser_port
./project_cmake 192.168.0.43 5001 //服務(wù)器主機的IP和是設(shè)置的端口號文章來源地址http://www.zghlxwxcb.cn/news/detail-485943.html
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define SERVER_PORT 5001 // 本地字節(jié)序
#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、創(chuàng)建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); // 將端口號從本地字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
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;
}
下面的代碼是上面的基礎(chǔ)將進行進行修改的,可以參考著看!
二、使用進程實現(xiàn)并發(fā)服務(wù)器
1、服務(wù)器端代碼
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define SERVER_PORT 5001 // 本地字節(jié)序
#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、創(chuàng)建socket 得到fd
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
printf("%s %d fd<0\n", __func__, __LINE__);
return 0;
}
// 優(yōu)化4 允許地址快速重用,當服務(wù)器斷開后可以快速重用
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); // 將端口號從本地字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
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); // 將主機從點分形式轉(zhuǎn)換為32字節(jié)
// 優(yōu)化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() 將主動套接字轉(zhuǎn)換為被動套接字
int reclisten = listen(fd, SERVER_BACKLOG); // 允許正在進行連接的客戶端數(shù)目
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;
}
// 創(chuàng)建一個子進程用于處理已建立連接的客戶的交互數(shù)據(jù)
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)))
{
// 為空取反 不為空 就執(zhí)行這里
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;
}
2、客戶端代碼
和上面一致,這里不贅述。文章來源:http://www.zghlxwxcb.cn/news/detail-485943.html
注意:
使用該函數(shù)時需要注意輸入?yún)?shù):
./project_cmake serv_ip ser_port
./project_cmake 192.168.0.43 5001 //服務(wù)器主機的IP和是設(shè)置的端口號
到了這里,關(guān)于C/S客戶端核服務(wù)端-并發(fā)服務(wù)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!