#include
#include
int socket(int domain, int type, int protocol);
功能:创建一个套接字文件,然后以文件形式来操作通信,不过套接字文件没有文件名。
参数:
domain
:用于指定协议族是IPV4还是IPV6,分别对应参数AF_INET
和参数AF_INET6
。type
:套接字类型,用于进一步指定使用协议族中的哪个子协议来通信:SOCK_STREAM
,表示使用TCP协议;SOCK_DGRAM
,表示使用UDP协议;SOCK_RDM
,表示使用原始网络通信,即IP协议;SOCK_NONBLOCK
,表示将socket返回的文件描述符指定为非阻塞的,它可以与前面的宏进行或运算。protocol
:表示传输协议,一般情况下可以直接写0,有操作系统自动推演出应该使用什么协议。返回值:成功返回套接字描述符,失败返回-1并设置errno。
#include
#include
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr_in
{
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填0 */
};
struct in_addr
{
unsigned long s_addr; /* IPV4的32位IP地址 */
};
功能:将指定了通信协议的套接字文件与IP以及端口绑定起来。
参数:
sockfd
:套接字的文件描述符。addr
:指定要绑定的参数信息。编程中一般并不直接针对sockaddr数据结构操作,而是使用与sockaddr等价的sockaddr_in数据结构。addrlen
:第二个参数的结构的长度。此外,由于网络字节序有一般采用大端(高尾端)排序方式,所以从主机向网络发送和从网络向主机接收时要进行字节序的转换:
#include
uint32_t htonl(uint32_t hostlong); /* host to network short */
uint16_t htons(uint16_t hostshort); /* host to network short */
uint32_t ntohl(uint32_t netlong); /* network to host long */
uint16_t ntohs(uint16_t netshort); /* network to host short */
同时,IP地址的格式也要在字符串和32位整数之间相互转化:
#include
#include
#include
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
char *inet_ntoa(struct in_addr in);
struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);
in_addr_t inet_lnaof(struct in_addr in);
in_addr_t inet_netof(struct in_addr in);
#include
#include
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
如果是服务器主动关闭连接,默认需要等待2MSL才能将端口资源释放,这会导致此端口短时间内无法重用,于是我们通过下面这段函数将该socket设置为可重用:
/* 设置套接字为可重用 */
int option = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
#include
#include
int listen(int sockfd, int backlog);
功能:将套接字文件描述符,从主动文件描述符变为被动文件描述符,然后用于被动监听客户的连接。
参数:
sockfd
:socket返回的套接字文件描述符。backlog
:指定的队列容量,这个队列用于记录正在连接,但是还没连接完成的客户,一般将队列容量指定为2、3即可。这个容量并没有什么统一的设定值,一般来说只要小于30即可。#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#define _GNU_SOURCE
#include
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);
功能:被动监听客户发起三次握手的连接请求,三次握手成功,即建立连接成功。accept被动监听客户连接的过程其实也是监听客户上线的过程。对于那些只连接了一般,还未连接完成的客户,会被记录到未完成队列中,队列的容量由listen函数的第二个参数backlog来指定。服务端调用accept函数监听连接,客户端调用connect来请求连接。一旦连接成功,服务器这边的TCP协议会记录客户的IP和端口,如果是跨网通信的,记录IP的就是客户所在的路由器的公网IP。
参数:
sockfd
:一个已经通过listen函数转化的被动套接字文件描述符。addr
:用于记录发起连接请求的那个客户端的IP和端口。addrlen
:用一个变量记录第二个参数的长度,addrlen对应这个变量的地址。返回值:如果执行成功返回新的套接字文件描述符,否则返回-1并设置errno。
#include
#include
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:向服务器主动发起连接请求,即主动发起三次握手。
参数:
sockfd
:socket函数所返回的套接字文件描述符。addr
:用于设置你所要连接的服务器的IP和端口。如果只是纯粹的局域网内部通信的话,IP就是局域网IP,但如果是跨网通信的话,IP必须是服务器所在的路由器的公网IP。为了方便操作,在应用层我们还是使用struct sockaddr_in
来设置,然后传递给connect时在强制转换为struct sockaddr
。addrlen
:第二个参数所指定的结构体变量的大小。#include
#include
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
功能:向对方发送数据或从对方接收数据。
参数:
sockfd
:用于通信的通信描述符。通信描述符在客户端对应socket函数生成的套接字描述符,在服务端则是accept返回的套接字描述符。buf
:应用缓存,存放要发送的或待接收的数据,一般情况下为一个结构体。flags
:一般置为0,表示阻塞发送或阻塞接收,如果想要非阻塞读取或发送则置为MSG_DONTWAIT
。#include
#include
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
功能:发送或接收数据,最后两个参数均为NULL时功能与send和recv相同。
参数:
sockfd
:socket返回的套接字。对于UDP来说,套接字描述符直接用于通信。buf
:存放数据的应用缓存。len
:应用缓存的大小。flags
:一般写0,表示阻塞发送数据。dest_addr
&src_addr
:IP地址以及端口等套接字信息。由于UDP不面向连接,无法自动记录对方的套接字信息,所以每次数据的发送都需要指定套接字信息。addrlen
:套接字信息结构体的大小。返回值:成功返回发送或接收到的字节数,失败返回-1并设置errno。
#include
int shutdown(int sockfd, int how);
功能:可以按照要求关闭连接,而且不管有多少个描述符指向同一个连接,shutdown可以一次性将其全部关闭。
参数:
sockfd
:服务端使用accept函数返回的描述符。how
:如何断开连接。SHUT_RD
只断开读连接,SHUT_WR
只断开写连接,SHUT_RDWR
将读、写连接全部断开。shutdown
与close
的区别:
#include
int epoll_create(int size);
int epoll_create1(int flags);
功能:epoll_create()
函数用于创建一个epoll实例,它实际上是由一个红黑树来实现的。
参数:
size
:用于告诉内核这个epoll需要关注的描述符的大致数目而不是最大个数。实际上Linux在2.6.8版本以后会忽略这个参数,但这个参数必须大于零。当容量不足时epoll会自动扩容。返回值:返回创建的epoll红黑树的文件描述符。
#include
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
功能:用于操作创建的epoll实例,主要包括增加、修改和删除三种操作。
参数:
epfd
:epoll的文件描述符。op
:对epoll执行的操作,主要包括:EPOLL_CTL_ADD
,向epoll中注册新的文件描述符。EPOLL_CTL_MOD
,修改已注册文件描述符的监听事件。EPOLL_CTL_DEL
,从epoll中删除文件描述符。fd
:操作关联的文件描述符。event
:告诉内核要监听什么事件,实际上epoll的红黑树每个结点上都对应一个epoll_event类型的结构体。结构体中的成员events主要包括以下宏:EPOLLIN
:表示对应的文件描述符可以读。EPOLLOUT
:表示对应的文件描述符可以写。EPOLLPRI
:表示对应的文件描述符有紧急数据或者带外数据到来。EPOLLERR
:表示对应的文件描述符发生错误。EPOLLHUP
:表示对应的文件描述符被挂断。EPOLLET
:将epoll设置为边缘触发模式。EPOLLONESHOT
:只监听一次事件,当监听完这次事件后,如果还需要继续监听这个socket的话,需要将它再次加入到epoll队列里。返回值:执行成功返回0,否则返回-1并设置errno的值。
#include
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);
功能
参数
epfd
:epoll的文件描述符。events
:一个结构体指针,当监听的文件描述符发生改变时,内核会把发生了改变的epoll_event拷贝到这个参数里。maxevents
:数组的容量。timeout
:超时信息:-1
时表示永久阻塞。0
时表示立即返回。>0
时表示监听到目标数目的文件描述符时再返回。返回值:执行成功返回发出请求的文件描述符的个数,执行失败返回-1并设置error。
服务端 server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 2015
#define IP "192.168.179.129"
#define MAX_SIZE 1024
int main() {
int sockfd;//服务器套接字描述符
int client_sockfd;//通信套接字描述符
char buffer[MAX_SIZE];//数据缓冲区
struct sockaddr_in server_addr;//服务器套接字信息
struct sockaddr_in client_addr;//客户端套接字信息
/* 获取服务器套接字文件描述符 */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket error");
exit(1);
}
/* 初始化服务器套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;//使用IPv4
server_addr.sin_port = htons(PORT);//对端口进行字节序转化
server_addr.sin_addr.s_addr = inet_addr(IP);//对IP地址进行格式转化
/* 因为最后需要服务器主动关闭连接,所以要设置服务器套接字为可重用 */
int option = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
/* 绑定服务器套接字信息 */
if (bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {
perror("bind error");
exit(1);
}
/* 将服务器套接字转化为被动监听 */
if (listen(sockfd, 3) < 0) {
perror("listen error!");
exit(1);
}
/* 与客户端进行串行连接 */
while(1) {
printf("[server] Server is waiting······\n");
/* 等待客户端的连接 */
int len = sizeof(struct sockaddr_in);//通信套接字结构体长度
memset(&client_addr, 0, sizeof(struct sockaddr_in));
if ((client_sockfd = accept(sockfd, (struct sockaddr *)(&client_addr), &len)) < 0) {
perror("accept error");
exit(1);
}
printf("[server] Client's port is %d, ip is %s\n", ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));
/* 接收客户端的消息 */
memset(buffer, 0, MAX_SIZE);
recv(client_sockfd, buffer, sizeof(buffer), 0);
printf("[server] Client's message:%s\n", buffer);
/* 向客户端发送消息 */
send(client_sockfd, "I have received your message.", 30, 0);
/* 关闭连接 */
shutdown(client_sockfd, SHUT_RDWR);
}
return 0;
}
客户端 client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 2015
#define IP "192.168.179.129"
#define MAX_SIZE 1024
int main() {
int sockfd;//客户端套接字描述符
char buffer[MAX_SIZE];//收发数据缓冲区
struct sockaddr_in server_addr;//服务器套接字描述符
/* 获取客户端套接字文件描述符 */
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket error");
exit(1);
}
/* 初始化服务器套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
/* 向服务器发送连接请求 */
if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {
perror("connect error");
exit(1);
}
printf("[client] Connect successfully.\n");
/* 向服务器发送消息 */
printf("[client] Please input your message>>>");
memset(buffer, 0, MAX_SIZE);
scanf("%[^\n]%*c", buffer);
send(sockfd, buffer, strlen(buffer), 0);
/* 接收服务端的消息 */
memset(buffer, 0, MAX_SIZE);
recv(sockfd, buffer, sizeof(buffer), 0);
printf("[client] Server's message:%s\n", buffer);
return 0;
}
运行结果
服务端 server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CLIENT_PORT 2015
#define SERVER_PORT 2025
#define IP "192.168.179.129"
#define MAX_SIZE 1024
int main() {
int sockfd;//服务端套接字描述符
char buffer[MAX_SIZE];//收发数据缓冲区
struct sockaddr_in client_addr;//客户端套接字信息
struct sockaddr_in server_addr;//服务端套接字信息
/* 获取服务端套接字描述符 */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket error");
exit(1);
}
/* 初始化服务端套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = (SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
/* 绑定接收端信息 */
if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in)) < 0) {
perror("bind error");
exit(1);
}
/* 接收消息 */
while (1) {
/* 初始化客户端套接字信息 */
memset(&client_addr, 0, sizeof(struct sockaddr_in));
client_addr.sin_family = AF_INET;
client_addr.sin_port = (CLIENT_PORT);
client_addr.sin_addr.s_addr = inet_addr(IP);
/* 收发消息 */
printf("[server] Srever is waiting······\n");
int len = sizeof(struct sockaddr_in);
memset(buffer, 0, sizeof(buffer));
recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &len);
printf("[server] Client's port is %d, ip is %s.\n", ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));
printf("[server] Client's message:%s\n", buffer);
memset(buffer, 0, sizeof(buffer));
sendto(sockfd, "I have received your message.", 30, 0, (struct sockaddr *) &client_addr, sizeof(struct sockaddr_in));
}
return 0;
}
客户端 client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define CLIENT_PORT 2015
#define SERVER_PORT 2025
#define IP "192.168.179.129"
#define MAX_SIZE 1024
int main() {
int sockfd;//客户端套接字描述符
char buffer[MAX_SIZE];//收发数据缓冲区
struct sockaddr_in client_addr;//客户端套接字信息
struct sockaddr_in server_addr;//服务端套接字信息
/* 获取客户端套接字描述符 */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket error");
exit(1);
}
/* 初始化服务端套接字信息 */
memset(&client_addr, 0, sizeof(struct sockaddr_in));
client_addr.sin_family = AF_INET;
client_addr.sin_port = (CLIENT_PORT);
client_addr.sin_addr.s_addr = inet_addr(IP);
/* 绑定接收端信息 */
if (bind(sockfd, (struct sockaddr *) &client_addr, sizeof(struct sockaddr_in)) < 0) {
perror("bind error");
exit(1);
}
/* 接收消息 */
while (1) {
/* 初始化服务端套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = (SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
/* 收发消息 */
printf("[client] Please input your message>>>");
memset(buffer, 0, sizeof(buffer));
scanf("%[^\n]%*c", buffer);
sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));
int len = sizeof(struct sockaddr_in);
recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *) &server_addr, &len);
printf("[client] Server's port is %d, ip is %s.\n", ntohs(server_addr.sin_port), inet_ntoa(server_addr.sin_addr));
printf("[client] Server's message:%s\n", buffer);
memset(buffer, 0, sizeof(buffer));
}
return 0;
}
运行结果
服务端 server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 2015
#define IP "192.168.179.129"
#define MAX_SIZE 1024
void *recv_thread_fun(void *arg);
int main() {
int server_sockfd;//服务器套接字描述符
int client_sockfd;//通信套接字描述符
pthread_t recv_thread_id;
struct sockaddr_in server_addr;//服务器套接字信息
struct sockaddr_in client_addr;//客户端套接字信息
/* 获取服务器套接字文件描述符 */
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (server_sockfd == -1) {
perror("socket error");
exit(1);
}
/* 初始化服务器套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;//使用IPv4
server_addr.sin_port = htons(PORT);//对端口进行字节序转化
server_addr.sin_addr.s_addr = inet_addr(IP);//对IP地址进行格式转化
/* 因为最后需要服务器主动关闭连接,所以要设置服务器套接字为可重用 */
int option = 1;
setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
/* 绑定服务器套接字信息 */
if (bind(server_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {
perror("bind error");
exit(1);
}
/* 将服务器套接字转化为被动监听 */
if (listen(server_sockfd, 3) < 0) {
perror("listen error!");
exit(1);
}
/* 与客户端进行串行连接 */
while(1) {
printf("[server] Server is waiting······\n");
/* 等待客户端的连接 */
int len = sizeof(struct sockaddr_in);//通信套接字结构体长度
memset(&client_addr, 0, sizeof(struct sockaddr_in));
if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)(&client_addr), &len)) < 0) {
perror("accept error");
exit(1);
}
printf("[server] Client %d port is %d, ip is %s.\n", client_sockfd,ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));
/* 创建子线程用于收发消息 */
if (pthread_create(&recv_thread_id, NULL, recv_thread_fun, (void *) &client_sockfd) != 0) {
perror("pthread create error");
exit(1);
}
}
return 0;
}
/* 子线程运行函数 */
void *recv_thread_fun(void *arg) {
int client_sockfd = *((int *) arg);
char buffer[MAX_SIZE];//数据缓冲区
pthread_detach(pthread_self());//将本线程转换为分离态,由系统自动回收资源
while (1) {
/* 接收客户端的消息 */
memset(buffer, 0, MAX_SIZE);
if (recv(client_sockfd, buffer, sizeof(buffer), 0) == 0) {
/* 监测客户端的退出 */
printf("[server] Client %d has exited.\n", client_sockfd);
pthread_exit(NULL);
}
printf("[server] Client %d message:%s\n", client_sockfd, buffer);
/* 向客户端发送消息 */
send(client_sockfd, "I have received your message.", 30, 0);
}
}
客户端 client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 2015
#define IP "192.168.179.129"
#define MAX_SIZE 1024
int main() {
int client_sockfd;//客户端套接字描述符
char buffer[MAX_SIZE];//收发数据缓冲区
struct sockaddr_in server_addr;//服务器套接字描述符
/* 获取客户端套接字文件描述符 */
if((client_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket error");
exit(1);
}
/* 初始化服务器套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
/* 向服务器发送连接请求 */
if (connect(client_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {
perror("connect error");
exit(1);
}
printf("[client] Connect successfully.\n");
while (1) {
/* 向服务器发送消息 */
printf("[client] Please input your message>>>");
memset(buffer, 0, MAX_SIZE);
scanf("%[^\n]%*c", buffer);
if (!buffer[0]) break;//输入空代表结束通信
send(client_sockfd, buffer, strlen(buffer), 0);
/* 接收服务端的消息 */
memset(buffer, 0, MAX_SIZE);
recv(client_sockfd, buffer, sizeof(buffer), 0);
printf("[client] Server's message:%s\n", buffer);
}
/* 关闭连接 */
shutdown(client_sockfd, SHUT_RDWR);
return 0;
}
运行结果
服务端 server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 1521
#define IP "0.0.0.0"
#define MAX_SIZE 1024
int main() {
int server_sockfd;//服务器套接字描述符
int client_sockfd;//通信套接字描述符
struct sockaddr_in server_addr;//服务器套接字信息
struct sockaddr_in client_addr;//客户端套接字信息
/* 获取服务器套接字文件描述符 */
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (server_sockfd == -1) {
perror("socket error");
exit(1);
}
/* 初始化服务器套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;//使用IPv4
server_addr.sin_port = htons(SERVER_PORT);//对端口进行字节序转化
server_addr.sin_addr.s_addr = inet_addr(IP);//对IP地址进行格式转化
/* 因为最后需要服务器主动关闭连接,所以要设置服务器套接字为可重用 */
int option = 1;
setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
/* 绑定服务器套接字信息 */
if (bind(server_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {
perror("bind error");
exit(1);
}
/* 将服务器套接字转化为被动监听 */
if (listen(server_sockfd, 3) < 0) {
perror("listen error!");
exit(1);
}
/* 与客户端进行串行连接 */
while(1) {
printf("[server] Server is waiting······\n");
/* 等待客户端的连接 */
int len = sizeof(struct sockaddr_in);//通信套接字结构体长度
memset(&client_addr, 0, sizeof(struct sockaddr_in));
if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)(&client_addr), &len)) < 0) {
perror("accept error");
exit(1);
}
printf("[server] Client %d port is %d, ip is %s.\n", client_sockfd,ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));
int pid = fork();//创建子进程用于收发消息
if (pid > 0) {
close(client_sockfd);
} else if (pid == 0) {
char buffer[1024];
while (1) {
/* 接收客户端的消息 */
memset(buffer, 0, MAX_SIZE);
if (recv(client_sockfd, buffer, sizeof(buffer), 0) == 0) {
/* 监测客户端的退出 */
printf("[server] Client %d has exited.\n", client_sockfd);
exit(0);
}
printf("[server] Client %d message:%s\n", client_sockfd, buffer);
/* 向客户端发送消息 */
send(client_sockfd, "I have received your message.", 30, 0);
}
} else {
perror("fork error");
exit(1);
}
}
return 0;
}
客户端 client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 1521
#define IP "120.78.188.0"
#define MAX_SIZE 1024
int main() {
int client_sockfd;//客户端套接字描述符
char buffer[MAX_SIZE];//收发数据缓冲区
struct sockaddr_in server_addr;//服务器套接字描述符
/* 获取客户端套接字文件描述符 */
if((client_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket error");
exit(1);
}
/* 初始化服务器套接字信息 */
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = inet_addr(IP);
/* 向服务器发送连接请求 */
if (connect(client_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {
perror("connect error");
exit(1);
}
printf("[client] Connect successfully.\n");
while (1) {
/* 向服务器发送消息 */
printf("[client] Please input your message>>>");
memset(buffer, 0, MAX_SIZE);
scanf("%[^\n]%*c", buffer);
if (!buffer[0]) break;//输入空代表结束通信
send(client_sockfd, buffer, strlen(buffer), 0);
/* 接收服务端的消息 */
memset(buffer, 0, MAX_SIZE);
recv(client_sockfd, buffer, sizeof(buffer), 0);
printf("[client] Server's message:%s\n", buffer);
}
/* 关闭连接 */
shutdown(client_sockfd, SHUT_RDWR);
return 0;
}
运行结果