采用TCP协议的C/S架构示例(1)
2018.12.18
本文是一个TCP通讯的示例,分为服务器和客户端两部分。
服务器端47.98.140.167创建套接字socket,并与端口11014绑定;
然后使套接字处于监听listen状态,调用accept等待来自客户端的连接请求;
收到客户端的连接请求后与客户端建立连接;
最后接收客户端发来的消息并打印出来。
客户端创建套接字socket,然后连接connect到服务器47.98.140.167的11014端口;
使用connect返回的连接套接字与服务器通信,交换数据。
一、模块封装
我们将一些通用的代码封装起来,便于使用。
通用网络封装代码头文件 tcp_net_socket.h。
#ifndef __TCP__NET__SOCKET__H
#define __TCP__NET__SOCKET__H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
extern int tcp_init(const char* ip, int port);
extern int tcp_accept(int sfd);
extern int tcp_connect(const char* ip, int port);
extern void signalhandler(void);
#endif
具体的函数封装如下(tcp_net_socket.c)。
#include \"tcp_net_socket.h\"
#define LISTENQ 12 // 最多监听LISTENQ个连接
int tcp_init(const char* ip, int port) //服务器端套接字的初始化
{
// create a new socket
int sfd;
if((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror(\"socket\");
exit(-1);
}
//允许重复使用port与套接字进行绑定
int optval = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, (void *)&optval, sizeof(int)) == -1) {
perror(\"setsockopt\");
close(sfd);
exit(-1);
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
//将socket与指定的ip、port绑定
if(bind(sfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in)) == -1) {
perror(\"bind\");
close(sfd);
exit(-1);
}
// 最多监听LISTENQ个连接
if(listen(sfd, LISTENQ) == -1) {
perror(\"listen\");
close(sfd);
exit(-1);
}
return sfd;
}
/* 用于服务器端的接收,返回新的代表客户端的套接字
* 之后可以利用这个新的套接字与客户端交换数据
* sfd套接字继续等待客户端的连接请求
*/
int tcp_accept(int sfd) //用于服务器端的接收
{
struct sockaddr_in cli_addr;
memset(&cli_addr, 0, sizeof(struct sockaddr_in));
int cli_addr_len = sizeof(struct sockaddr_in);
//sfd接受客户端连接,并创建新的socket为new_fd,
//将请求连接的客户端IP、port保存在结构体cli_addr中
int new_fd;
if((new_fd = accept(sfd, (struct sockaddr *)&cli_addr, &cli_addr_len)) == -1) {
perror(\"accept\");
close(sfd);
exit(-1);
}
printf(\"%s:%d connect come in!\\n\", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
return new_fd;
}
int tcp_connect(const char* ip, int port) //用于客户端的连接
{
int sfd;
if((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { //注册新的socket
perror(\"socket\");
exit(-1);
}
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
//将sfd连接至指定的服务器网络地址serv_addr
if(connect(sfd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr_in)) == -1) {
perror(\"connect\");
close(sfd);
exit(-1);
}
return sfd;
}
void signalhandler(void) //用于信号处理,让服务器端在按下Ctrl+C或Ctrl+\\时不会退出
{
sigset_t sigSet;
sigemptyset(&sigSet);
sigaddset(&sigSet, SIGINT);
sigaddset(&sigSet, SIGQUIT);
sigprocmask(SIG_BLOCK, &sigSet, NULL);
}
二、服务器端的代码
服务器端:tcp_net_server.c
#include \"tcp_net_socket.h\"
#define REVBUF 512
int main(int argc, char* argv[])
{
if(argc < 3) {
printf(\"Usage: ./servertcp ip port\\n\");
exit(-1);
}
// signalhandler();
/* eg: int sfd = tcp_init(\"192.168.1.130\", 8888); */
int sfd = tcp_init(argv[1], atoi(argv[2]));
char revbuf[REVBUF] = {0};
int revbytes;
int cfd = tcp_accept(sfd);
while(1) {
revbytes = recv(cfd, revbuf, sizeof(revbuf), 0); // received date from client cfd, save data to revbuf[]
if(revbytes == -1) {
perror(\"recv\");
close(cfd);
close(sfd);
exit(-1);
} else if(revbytes == 0) { //if client closed, ret == 0
close(cfd);
break;
}
printf(\"$ %s\", revbuf);
// char str[] = \"hello world!\";
// if(send(cfd, str, strlen(buf), 0) == -1) { //send str[] to client cfd
if(send(cfd, revbuf, revbytes, 0) == -1) {
perror(\"send\");
close(cfd);
close(sfd);
exit(-1);
}
memset(revbuf, 0, sizeof(revbuf));
}
close(cfd);
close(sfd);
return 0;
}
三、客户端的代码
客户端:tcp_net_client.c
#include \"tcp_net_socket.h\"
int main(int argc, char* argv[])
{
if(argc < 3) {
printf(\"Usage: ./clienttcp ip port\\n\");
exit(-1);
}
/* eg: int sfd = tcp_connect(\"192.168.1.130\", 8888); */
int sfd = tcp_connect(argv[1], atoi(argv[2]));
/*
char buf[512] = {0};
send(sfd, \"hello\", 6, 0); //send \"hello\" to sfd server
recv(sfd, buf, sizeof(buf), 0); //receive data from sfd server
puts(buf);
close(sfd);
*/
char sbuf[1024] = {0};
char rbuf[1024] = {0};
printf(\"$ \");
while(fgets(sbuf, sizeof(sbuf), stdin) != NULL) {
send(sfd, sbuf, strlen(sbuf), 0);
recv(sfd, rbuf, sizeof(rbuf), 0);
printf(\"# %s$ \", rbuf);
memset(sbuf, 0, sizeof(sbuf));
memset(rbuf, 0, sizeof(rbuf));
}
printf(\"close sfd\\n\");
close(sfd);
}
四、编译
gcc -o tcp_net_server tcp_net_server.c tcp_net_socket.c
gcc -o tcp_net_client tcp_net_client.c tcp_net_socket.c
如果客户端是ARM40-A5,需将gcc换成相应的arm gcc。
五、执行
先执行服务器端的程序:
./tcp_net_server 192.168.1.130 11014
在执行客户端程序:
./tcp_net_client 192.168.1.130 11014
在客户端输入:
./tcp_net_client 192.168.1.130 11014
$ 123
# 123
$ 345
# 345
$ 456
# 456
在服务器端得到:
./tcp_net_server 192.168.1.130 8888
192.168.1.130:55794 connect OK!
$ 123
$ 345
$ 456
这种方式只能建立一个连接,希望建立多个连接,见《采用TCP协议的C/S架构示例(2)》。
参考文章
《高质量嵌入式Linux C编程》
《从实践中学嵌入式Linux应用程序开发》
《UNIX环境高级编程第3版》
继续阅读与本文标签相同的文章
上一篇 :
VMware虚拟机安装Linux系统详细过程
-
韩国公布500亿美元计划 大力发展电动和自动驾驶汽车
2026-05-18栏目: 教程
-
原来这样做可以提高自媒体短视频的播放量?
2026-05-18栏目: 教程
-
用了3年以上的iPhone手机,应该这样清理手机缓存,很实用
2026-05-18栏目: 教程
-
一文弄懂,锁的基本概念到Redis分布式锁实现
2026-05-18栏目: 教程
-
阿里云混合云备份如何还原虚拟机备份?
2026-05-18栏目: 教程
