Linux高阶——1118—TCP客户端服务端
1、sock.h
socket常用函数
int ACCEPT(int,struct sockaddr*,socklen_t *);
int SOCKET(int,int,int);
ssize_t RECV(int,void*,size_t,int);
int CONNECT(int, const struct sockaddr *,socklen_t);
int BIND(int, const struct sockaddr *,socklen_t);
int LISTEN(int, int backlog);
ssize_t SEND(int, const void *, size_t, int);
网络初始化函数
int net_initializer(const char*,int,int);
首次响应函数
int first_response(client_info);
测试IO处理函数
int test_business(int);
获取时间函数
int get_time(char* tm);
总代码
#ifndef _MYSOCK_
#define _MYSOCK_
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<netdb.h>
#include<errno.h>
#include<time.h>
#include<stdio.h>
#endif
typedef struct
{
int sockfd;
int port;
char ip[16];
}client_info;
int ACCEPT(int,struct sockaddr*,socklen_t *);
int SOCKET(int,int,int);
ssize_t RECV(int,void*,size_t,int);
int CONNECT(int, const struct sockaddr *,socklen_t);
int BIND(int, const struct sockaddr *,socklen_t);
int LISTEN(int, int backlog);
ssize_t SEND(int, const void *, size_t, int);
int net_initializer(const char*,int,int);
int first_response(client_info);
int test_business(int);
int get_time(char* tm);
2、sock.c
SOCKET()
SOCKET(socket协议域,数据传输层使用协议模式,指定具体的协议)
int SOCKET(int domain,int type,int protocal)
{
int fd;
if((fd=socket(domain,type,protocal))==-1)
{
perror("socket call failed");
return fd;
}
return fd;
}
ACCEPT()——服务端使用这个函数等待客户端连接
ACCEPT(服务端socket,客户端的网络信息结构体,客户端网络信息结构体长度) ——客户端接收数据
int ACCEPT(int sockfd,struct sockaddr* addr,socklen_t * addrlen)
{
int fd;
if((fd=accept(sockfd,addr,addrlen))==-1)
{
perror("accept call failed");
return fd;
}
return fd;
}
CONNECT()——客户端使用这个函数连接服务端
客户端只有一个socket
CONNECT(用于连接的socket,目标信息结构体,目标信息结构体长度)
int CONNECT(int sockfd, const struct sockaddr *addr,socklen_t addrlen)
{
int fd;
if((fd=connect(sockfd,addr,addrlen))==-1)
{
perror("connect call failed");
return fd;
}
return fd;
}
BIND()——一般只有服务端使用
BIND(需要绑定的socket,网络信息结构体的地址,网络信息结构体的长度)
int BIND(int sockfd, const struct sockaddr *addr,socklen_t addrlen)
{
int fd;
if((fd=bind(sockfd,addr,addrlen))==-1)
{
perror("bind call failed");
return fd;
}
return fd;
}
LISTEN()——服务端使用
LISTEN(服务端socket,等待连接队列的最大长度)
int LISTEN(int sockfd, int backlog)
{
int fd;
if((fd=listen(sockfd, backlog))==-1)
{
perror("listen call failed");
return fd;
}
return fd;
}
RECV()
RECV(套接字sockfd,数据buf,数据长度size,选项,发送的长度)
对于服务端,sockfd是accept函数的返回值
对于客户端,sockfd是connect函数的返回值
ssize_t RECV(int sockfd,void* buf,size_t len,int flag)
{
ssize_t fd;
if((fd=recv(sockfd,buf,len,flag))==-1)
{
perror("recv call failed");
return fd;
}
return fd;
}
SEND()
SEND(发送的人的sockfd,数据buf,数据包长度)
ssize_t SEND(int sockfd, const void *buf, size_t len, int flags)
{
ssize_t fd;
if((fd=send(sockfd,buf,len,flags))==-1)
{
perror("send call failed");
return fd;
}
return fd;
}
net_initializer()——网络初始化函数
net_initializer(ip地址,端口号,可以监听队列的最大长度)
int net_initializer(const char* ip,int port,int backlog)
{
int sockfd;
struct sockaddr_in addr;
bzero(&addr,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=inet_addr("82.157.31.74");
sockfd=SOCKET(AF_INET,SOCK_STREAM,0);
BIND(sockfd,(struct sockaddr*)&addr,sizeof(addr));
LISTEN(sockfd,backlog);
return sockfd;
}
get_time()——获取时间函数
get_time(时间数组)
int get_time(char* tm)
{
bzero(tm,1024);
time_t tp;
tp=time(NULL);
ctime_r(&tp,tm);
tm[strlen(tm)-1]='\0';
return 0;
}
first_response()——首次适应函数
first_response(定义的结构体)
client_info——定义的结构体——包含port,ip,和创建的套接字
typedef struct
{
int sockfd;
int port;
char ip[16];
}client_info;
int first_response(client_info cf)
{
char response[1500];
bzero(response,1500);
char tm[1024];
get_time(tm);
printf("Server,output info,client ip %s,client port %d\n",cf.ip,cf.port);
SEND(cf.sockfd,response,strlen(response),MSG_NOSIGNAL);
return 0;
}
总代码
#include<sock.h>
int ACCEPT(int sockfd,struct sockaddr* addr,socklen_t * addrlen)
{
int fd;
if((fd=accept(sockfd,addr,addrlen))==-1)
{
perror("accept call failed");
return fd;
}
return fd;
}
int SOCKET(int domain,int type,int protocal)
{
int fd;
if((fd=socket(domain,type,protocal))==-1)
{
perror("socket call failed");
return fd;
}
return fd;
}
ssize_t RECV(int sockfd,void* buf,size_t len,int flag)
{
ssize_t fd;
if((fd=recv(sockfd,buf,len,flag))==-1)
{
perror("recv call failed");
return fd;
}
return fd;
}
int CONNECT(int sockfd, const struct sockaddr *addr,socklen_t addrlen)
{
int fd;
if((fd=connect(sockfd,addr,addrlen))==-1)
{
perror("connect call failed");
return fd;
}
return fd;
}
int BIND(int sockfd, const struct sockaddr *addr,socklen_t addrlen)
{
int fd;
if((fd=bind(sockfd,addr,addrlen))==-1)
{
perror("bind call failed");
return fd;
}
return fd;
}
int LISTEN(int sockfd, int backlog)
{
int fd;
if((fd=listen(sockfd, backlog))==-1)
{
perror("listen call failed");
return fd;
}
return fd;
}
ssize_t SEND(int sockfd, const void *buf, size_t len, int flags)
{
ssize_t fd;
if((fd=send(sockfd,buf,len,flags))==-1)
{
perror("send call failed");
return fd;
}
return fd;
}
int net_initializer(const char* ip,int port,int backlog)
{
int sockfd;
struct sockaddr_in addr;
bzero(&addr,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=inet_addr("82.157.31.74");
sockfd=SOCKET(AF_INET,SOCK_STREAM,0);
BIND(sockfd,(struct sockaddr*)&addr,sizeof(addr));
LISTEN(sockfd,backlog);
return sockfd;
}
int get_time(char* tm)
{
bzero(tm,1024);
time_t tp;
tp=time(NULL);
ctime_r(&tp,tm);
tm[strlen(tm)-1]='\0';
return 0;
}
int first_response(client_info cf)
{
char response[1500];
bzero(response,1500);
char tm[1024];
get_time(tm);
printf("Server,output info,client ip %s,client port %d\n",cf.ip,cf.port);
SEND(cf.sockfd,response,strlen(response),MSG_NOSIGNAL);
return 0;
}
3、生成网络动态库
将所有的.c或.cpp变成可重定位二进制文件(载入库数据时,查找可用内存,而不是固定地址)
生成可重定位的二进制文件.o——gcc test.c -fPIC -I头文件路径 -c
gcc sock.c -I./ -fPIC -c
生成.so库——gcc xxx.o -shared -fPIC -I头文件 -o libmysock.so
共享库名,线程库里为-lpthread,内存中为libpthread.so
gcc sock.o -shared -fPIC -I./ -o libmock.so
生成库后,需要使用-L指定库位置——gcc x.c -I头文件 -L 库路径 -lmysock -o app
如果共享库已经在默认/usr/lib位置,编译时无需-L参数
查看程序所依赖的库文件,查看未加载成功的库名——ldd 程序名
ldd app
将共享库.so文件复制到/usr/lib/——mv libname.so /usr/lib/
在linux操作系统下,使用ifconfig命令查看本机私有ip地址,如果本机写服务器使用本机ip
成功后,可以直接使用./程序名运行程序
客户端代码
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#define server_ip "192.168.5.133"
#define server_port 9090
int main()
{
struct sockaddr_in client_addr;
bzero(&client_addr,sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(server_port);
client_addr.sin_addr.s_addr = inet_addr(server_ip);
inet_pton(AF_INET,server_ip,&client_addr.sin_addr.s_addr);
int client_sock;
if((client_sock=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("sock create failed");
return -1;
}
socklen_t addrlen;
char recvv[1024];
printf("TCP IO Client Running...\n");
addrlen = sizeof(client_addr);
if((connect(client_sock, (struct sockaddr*)&client_addr, addrlen))==-1)
{
perror("failed");
return -1;
}
bzero(recvv,sizeof(recvv));
printf("client sock %d\n",client_sock);
int len=recv(client_sock,recvv,sizeof(recvv),0);
if(len==-1)
perror("recv call failed");
printf("len %d %s\n",len, recvv);
close(client_sock);
}
服务端代码
#include<sock.c>
#define server_ip "192.168.5.133"
#define server_port 9090
int main()
{
struct sockaddr_in client_addr;
int server_sock;
int client_sock;
server_sock=net_initializer(NULL,server_port,128);
socklen_t addrlen;
printf("TCP IO Servers Running...\n");
char cip[16];
ssize_t len;
client_info cf;
char buf[1500];
char* msg="Please try again\n";
char tm[1024];
while(1)
{
addrlen=sizeof(client_addr);
client_sock=ACCEPT(server_sock,(struct sockaddr*)&client_addr,&addrlen);
cf.sockfd=client_sock;
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,cf.ip,16);
cf.port=ntohs(client_addr.sin_port);
first_response(cf);
while((len=RECV(client_sock,buf,sizeof(buf),0))>0)
{
if((strcmp(buf,"time\n"))==0)
{
get_time(tm);
SEND(client_sock,tm,strlen(tm),MSG_NOSIGNAL);
bzero(tm,sizeof(tm));
}
else
{
SEND(client_sock,msg,strlen(msg),MSG_NOSIGNAL);
}
bzero(buf,sizeof(buf));
}
if(len==0)
{
printf("client exit\n");
close(client_sock);
}
}
close(server_sock);
printf("server done\n");
return 0;
}