每次使用socket通信,都会有很对相似的操作。本文,会对TCP与UDP通信做一简单封装,并生成动态库。
代码my_socket.h
#ifndef __MY_SOCKET_H__#define __MY_SOCKET_H__#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <arpa/inet.h>#define IN#define OUT#define IN_OUT#define MY_TCP 1#define MY_UDP 2typedef struct sockaddr* pSA ;typedef struct sockaddr_in SA ;#define MY_ASSERT(flag,msg) ( (flag) ? NULL : ( fPRintf(stdout,msg), exit(EXIT_FAILURE) ) ) // NULL代表什么也不做void my_socket(OUT int *local_sfd, int protocal, char *local_ip, int local_port);void my_listen(int local_sfd, int backlog);void my_accept(OUT int *peer_sfd, int local_sfd, OUT pSA peer_addr, IN_OUT int *addr_len );void my_connect(int local_sfd, pSA peer_addr, int addr_len);void my_recv(OUT int *recv_len, int peer_sfd, IN_OUT void *base, int len);void my_send(OUT int *send_len, int peer_sfd, void *base, int len);void my_recvfrom(OUT int *recvfrom_len, int peer_sfd, IN_OUT void *base, int len, OUT pSA peer_addr, IN_OUT int *addr_len);void my_sendto(OUT int *sendto_len, int peer_sfd, OUT void *base, int len, pSA peer_addr, int addr_len);void my_close(int sfd);#endif
my_socket.c
/************************************************************************* > File Name: my_socket.c > Author: KrisChou > Mail:zhoujx0219@163.com > Created Time: Mon 01 Sep 2014 06:54:48 PM CST ************************************************************************//* 本代码用于在一台主机上模拟socket通信。因此IP地址对于server和client而言是一样的。 * 为了简化代码,此处即使是客户端,也提前分配好端口号。事实上,主动方的端口号可以由系统分配,不用提前绑定 * --> 无论server或者client,都会预先绑定本地socket *//* 本代码local_sfd代表本地socket描述符。 * 对于服务器而言,就是用于监听的socket; 对于客户端而言就是用于通信的socket * peer_sfd,代表与对方通信的socket描述符。 * 对于服务器而言,由accept以传出参数形式返回;对于客户端而言,就是本地socket */#include "my_socket.h"void my_socket(OUT int *local_sfd, int protocal, char *local_ip, int local_port){ MY_ASSERT(protocal == MY_TCP || protocal == MY_UDP, "wrong arg! protocal is MY_TCP or MY_UDP! /n"); /* 创建本地socket */ if(protocal == MY_TCP) { MY_ASSERT(-1 != (*local_sfd = socket(AF_INET, SOCK_STREAM, 0)), "tcp_socket init falure!/n"); }else if(protocal == MY_UDP) { MY_ASSERT(-1 != (*local_sfd = socket(AF_INET, SOCK_DGRAM, 0)), "udp_socket init failure!/n"); } /* 将本地联系方式bind到本地socket */ SA local_addr; memset(&local_addr, 0, sizeof(SA)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(local_port); local_addr.sin_addr.s_addr = inet_addr(local_ip); MY_ASSERT( 0 == bind(*local_sfd, (pSA)&local_addr, sizeof(SA)), "bind failure!/n");}/*----------------------------- 以下针对TCP ----------------------------------------------------- *//* server: listen + accept */void my_listen(int local_sfd, int backlog){ MY_ASSERT( 0 == listen(local_sfd, backlog), "listen failure!/n");}void my_accept(OUT int *peer_sfd, int local_sfd, OUT pSA peer_addr, IN_OUT int *addr_len ){ MY_ASSERT(-1 != (*peer_sfd = accept(local_sfd, peer_addr, addr_len)), "accept failure!/n");}/* client: connect */void my_connect(int local_sfd, pSA peer_addr, int addr_len){ int cnt = 0; // 10次连不上就退出程序 while(-1 == connect(local_sfd, peer_addr, addr_len)) { cnt++; if(cnt == 10) { printf("connect failure!/n"); exit(EXIT_FAILURE); } sleep(1); }}/* recv and send */void my_recv(OUT int *recv_len, int peer_sfd, IN_OUT void *base, int len){ MY_ASSERT(-1 != (*recv_len = recv(peer_sfd, base, len, 0)), "recv error! /n");}void my_send(OUT int *send_len, int peer_sfd, void *base, int len){ MY_ASSERT(-1 != (*send_len = send(peer_sfd, base, len, 0)), "send error! /n");}/*---------------------------- 以下针对UDP--------------------------------------------------------*/void my_recvfrom(OUT int *recvfrom_len, int peer_sfd, IN_OUT void *base, int len, OUT pSA peer_addr, IN_OUT int *addr_len){ MY_ASSERT(-1 != (*recvfrom_len = recvfrom(peer_sfd, base, len, 0, peer_addr, addr_len)), "recvfrom failure!/n");}void my_sendto(OUT int *sendto_len, int peer_sfd, OUT void *base, int len, pSA peer_addr, int addr_len){ MY_ASSERT(-1 != (*sendto_len = sendto(peer_sfd, base, len, 0, peer_addr, addr_len)), "sendto failure!/n");}/* close */void my_close(int sfd){ MY_ASSERT(0 == close(sfd), "close failure!/n");}生成动态库
gcc -fPIC -o my_socket.o -c my_socket.c //我的.h文件和.c文件在一个目录下,如果不在,请指定头文件位置gcc -shared -o libmy_socket.so.1.0 my_socket.o cp./libmy_socket.s0.1.0 /libcd /libln -s libmy_socket.so.1.0 libmy_socket.so
我个人将头文件放在主目录的include文件夹下,方便以后查找和使用。(/home/purple/include)
测试代码server.c
#include "my_socket.h"#define IP "192.168.153.128"#define PORT 8888int main(int argc, char *argv[]){ int fd_server, fd_client; int val; //用4个字节的地址空间来传数据 int len; my_socket(&fd_server, MY_TCP, IP, PORT); my_listen(fd_server,5); my_accept(&fd_client, fd_server, NULL, NULL); printf("accept success!/n"); while(1) { my_recv(&len, fd_client, (void*)&val, sizeof(val)); printf("recv data: %d/n", val); my_send(&len, fd_client, (void*)&val, sizeof(val)); printf("%d has sent!/n/n", val); } my_close(fd_client); my_close(fd_server); return 0;}
client.c
#include "my_socket.h"#define IP "192.168.153.128"#define MY_PORT 6666#define SERVER_PORT 8888int main(int argc, char *argv[]){ /* socket */ int fd_client; my_socket(&fd_client, MY_TCP, IP, MY_PORT); /* connect */ SA server_addr; memset(&server_addr,0,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = inet_addr(IP); my_connect(fd_client, (pSA)&server_addr, sizeof(SA)); printf("connect success!/n"); /* 发送一个数据,并从服务器端返回这个数据 */ int val_in,val_out,len; while(scanf("%d", &val_in) == 1) { my_send(&len,fd_client,(void*)&val_in,sizeof(int)); my_recv(&len,fd_client,(void*)&val_out,sizeof(int)); printf("recv fron server: %d/n", val_out); } my_close(fd_client); return 0; }
编译如下
gcc -o server server.c -lmy_socket -I/home/purple/includegcc -o client client.c -lmy_socket -I/home/purple/include
经测试没有问题。
注意短路运算(||)必须有返回值,而exit返回值为void,因此头文件中的宏定义,我们没有使用短路运算,而是使用了三目运算符。
新闻热点
疑难解答