概述
在上一篇文章中,介绍了基本TCP套接字编程的基础知识和基本函数
详情点击 : UNIX环境编程(c语言)–套接字–基本TCP套接字编程
这次我们将写一个服务器和客户端的程序作为实例,加深巩固知识
程序基本功能:
-
客户端在标准输入写入一行文本,发送给服务器
-
服务器在网络输入读取这行文本,在末尾添加字符串,重新发送给客户端
-
客户端接收来自服务器的文本,重新打印到标准输出
-
客户端输入exit后退出,服务器等待下一次连接
本实例只是最简单的服务器客户端程序,没有多进程 多线程 io复用等技术 以后更新到相关内容,再升级
客户端程序
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
void print_help(char *name);
int main(int argc, char **argv)
{
int rv = -1;
int clifd = -1;
int port = -1;
char *ip;
char buf[128] = "hello world";
struct sockaddr_in servaddr;
struct option opts[] = { // 参数解析
{"port", required_argument, NULL, 'P'},
{"ip", required_argument, NULL, 'i'},
{"help", required_argument, NULL, 'h'}
};
while( (rv = getopt_long(argc,argv, "p:i:hc::", opts, NULL)) != -1) // 命令行参数解析
{
switch(rv)
{
case 'p' :
port = atoi(optarg); // 参数获取 端口
break;
case 'i' :
ip = optarg; // 获取ip
break;
case 'h' :
print_help(argv[0]);
return 0;
case 'c' :
memcpy(buf, optarg, sizeof(optarg));
break;
}
}
clifd = socket(AF_INET, SOCK_STREAM, 0); // socket
if(clifd == -1)
{
printf(" socket error : %s \n", strerror(errno));
return -1;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(ip, &servaddr.sin_addr);
rv = connect(clifd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if( rv == -1)
{
printf("connect error : %s \n ", strerror(errno));
goto clear;
}
rv = write(clifd, buf, strlen(buf));
if(rv < 0 )
{
printf("write error : %s \n", strerror(errno));
goto clear;
}
rv = read(clifd, buf, sizeof(buf));
if(rv < 0)
{
printf("write error : %s \n", strerror(errno));
goto clear;
}
printf("connect succed [%s:%d], \n\n read succed :%s \n", ip, port, buf);
memset(buf, 0, sizeof(buf));
printf(" write :: \n ");
fgets(buf, sizeof(buf), stdin); //在标准输入获取字符串
while( strstr(buf, "exit") == NULL ) //当输入 exit 时退出循环
{
rv = write(clifd, buf, strlen(buf)); //向服务器写字符串
if(rv < 0 )
{
printf("write error : %s \n", strerror(errno));
goto clear;
}
memset(buf, 0, sizeof(buf));
rv = read(clifd, buf, sizeof(buf)); // 接收服务器返回的字符串
if(rv < 0)
{
printf("read error : %s \n", strerror(errno));
goto clear;
}
else if(rv == 0)
{
printf("connect error :%s \n ", strerror(errno));
goto clear;
}
printf(" read :%s.\n", buf);
memset(buf, 0, sizeof(buf));
printf(" write :: \n ");
fgets(buf, sizeof(buf), stdin);
}
clear:
close(clifd);
return 0;
}
void print_help(char *name)
{
printf("--port -p : ipv4 port \n --ip -i : addr \n");
}
服务器程序
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <getopt.h>
void print_help(char *name);
#define SERV_STR "GFX" //加在每个接收字符串后面的字符串
int main(int argc, char **argv)
{
int rv = -1;
int servfd = -1;
int clifd = -1;
int port = 12345;
char buf[1024];
struct sockaddr_in cliaddr;
socklen_t cliaddr_len;
struct sockaddr_in servaddr;
struct option opts[] = {
{"port", required_argument, NULL, 'P'},
{"help", required_argument, NULL, 'h'}
};
while( (rv = getopt_long(argc,argv, "p:h", opts, NULL)) != -1) // 命令行参数解析
{
switch(rv)
{
case 'p' :
port = atoi(optarg);
break;
case 'h' :
print_help(argv[0]);
return 0;
}
}
servfd = socket(AF_INET, SOCK_STREAM, 0);
if(servfd < 0)
{
printf(" socket error : %s \n", strerror(errno));
return -1;
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
rv = bind(servfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(rv == -1)
{
printf(" bind error :%s \n ", strerror(errno));
goto clear;
}
rv = listen(servfd, 13);
if(rv == -1)
{
printf(" listen error :%s \n ", strerror(errno));
goto clear;
}
while(1)
{
printf(" [%d]start accept new \n", servfd);
clifd = accept(servfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
if(clifd < 0)
{
printf("accept error :%s \n", strerror(errno));
continue;
}
while(1) //客户端连接成功,开始服务
{
memset(buf, 0, sizeof(buf));
rv = read(clifd, buf, sizeof(buf)); // 读取字符串
if( rv < 0 )
{
printf("read error : %s \n", strerror(errno));
close(clifd);
break;
}
else if( rv == 0)
{
printf("connect break [%s : %d] \n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
close(clifd);
break;
}
else if( rv > 0)
{
printf("read succed [%s : %d] : %s\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port), buf);
buf[strlen(buf)-1] = ' ';
strcat(buf, SERV_STR); // 在字符串后加一个标识字符串
rv = write(clifd, buf, strlen(buf)); // 返回给客户端
if(rv < 0)
{
printf(" write error : %s \n" , strerror(errno));
close(clifd);
continue;
}
printf(" write succed : %s\n", buf);
}
}
}
clear:
close(clifd);
close(servfd);
return 0;
}
void print_help(char *name)
{
printf("--port -p : ipv4 port \n --ip -i : addr \n");
}
运行 & 结果
我的服务器端运行在 阿里云的云服务器
客户端运行在我的电脑的虚拟机上的Ubuntu
注意:服务器端能够连接成功的前提
1, 两者的网络是连通的,可以使用ping命令测试是否能够连通
2, 服务器端的端口是需要开放的,不开放无法进行通信
如果这两个条件没有满足,就先去解决咯
客户端运行结果
服务器端运行结果