服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(void)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, n;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 20);
printf("Accepting connections ...\n");
while(1) {
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
n = read(connfd, buf, MAXLINE);
printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));
for(i = 0; i < n; i++)
buf[i] = toupper(buf[i]);
write(connfd, buf, n);
close(connfd);
}
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
char buf[MAXLINE];
int sockfd, n;
char *str;
if(argc != 2) {
fputs("usage: ./client message\n", stderr);
exit(1);
}
str = argv[1];
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
write(sockfd, str, strlen(str));
n = read(sockfd, buf, MAXLINE);
printf("Response from server:\n");
write(STDOUT_FILENO, buf, n);
close(sockfd);
return 0;
}
上面例子是最基本的tcp socket 流程,没有错误处理,下面写一个模块 wrap.c 给socket函数加上错误处理代码包装成新的函数;
服务端也不能接收多个客户端请求,使用fork()函数创建一个子进程专门服务客户端,而父进程专门负责监听端口,再使用sigaction()函数处理SIGCHLD信号调用wait清理僵尸进程;使用setsockopt设置socket描述符选项SO_REUSEADDR为1,允许创建端口号相同但ip地址不同的多个socket描述符。h
客户端输入也不是交互式的,将其改为交互式的,并且处理服务器端关闭连接后客户端继续写数据产生的SIFPIPE信号(打印“*******”,未做实际处理)。
wrap.h
#ifndef WRAP_H
#define WRAP_H
void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void Connect(int fd, struct sockaddr *sa, socklen_t salen);
void Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, void *ptr, size_t nbytes);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
ssize_t Readline(int fd, void *vptr, size_t maxlen);
void Close(int fd);
#endif
wrap.c
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
void perr_exit(const char *s)
{
perror(s);
exit(1);
}
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
int n;
again:
if ((n = accept(fd, sa, salenptr)) < 0) {
if ((errno == ECONNABORTED) || (errno == EINTR)) {
goto again;
} else {
perr_exit("accept error");
}
}
return n;
}
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
if(bind(fd, sa, salen) < 0){
perr_exit("bind error");
}
}
void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (connect(fd, sa, salen) < 0) {
perr_exit("connect error");
}
}
void Listen(int fd, int backlog)
{
if(listen(fd, backlog) < 0){
perr_exit("listen error");
}
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0) {
perr_exit("socket error");
}
return n;
}
ssize_t Read(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
again:
if((n = read(fd, ptr, nbytes)) == -1) {
if(errno == EINTR) {
goto again;
} else {
return -1;
}
}
return n;
}
ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
ssize_t n;
again:
if ((n = write(fd, ptr, nbytes)) == -1) {
if (errno == EINTR) {
goto again;
} else {
return -1;
}
}
return n;
}
ssize_t Readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0) {
if( (nread = read(fd, ptr, nleft)) < 0) {
if( errno == EINTR ) {
nread = 0;
}else if( nread == 0 ) {
break;
}
}
nleft -= nread;
ptr += nread;
}
return n - nleft;
}
ssize_t Writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwrittrn;
const char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0) {
if (( nwrittrn = write(fd, ptr, nleft)) <= 0) {
if (nwrittrn < 0 && errno == EINTR) {
nwrittrn = 0;
} else {
return -1;
}
}
nleft -= nwrittrn;
ptr += nwrittrn;
}
return n;
}
static ssize_t my_read(int fd, char *ptr) {
static int read_cnt;
static char *read_ptr;
static char read_buf[100];
if (read_cnt <= 0) {
again:
if((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
if (errno == EINTR) {
goto again;
}
return -1;
} else if(read_cnt == 0) {
return 0;
}
read_ptr = read_buf;
}
read_cnt --;
*ptr = *read_ptr++;
return 1;
}
ssize_t Readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 0; n < maxlen; n++) {
rc = my_read(fd, &c);
if (rc == 1) {
*ptr++ = c;
if (c == '\n') {
break;
}
}else if(rc == 0) {
*ptr = 0;
return n-1;
}else {
return -1;
}
}
*ptr = 0;
return n;
}
void Close(int fd)
{
if(close(fd) == -1) {
perr_exit("close error");
}
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
void handle_sig(int sig)
{
printf("child exit\n");
wait(NULL);
}
int main(void)
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
int i, n;
struct sigaction act;
act.sa_handler = handle_sig;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGCHLD,&act,NULL);
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(listenfd, 20);
printf("Accepting connections ...\n");
while(1) {
cliaddr_len = sizeof(cliaddr);
connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
n = fork();
if ( n == -1) {
perror("call to fork");
exit(1);
} else if (n == 0) { // child
Close(listenfd);
while(1) {
n = Read(connfd, buf, MAXLINE);
if (n == 0) {
printf("the other side has been closed.\n");
break;
}
printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port));
for(i = 0; i < n; i++)
buf[i] = toupper(buf[i]);
Write(connfd, buf, n);
}
Close(connfd);
exit(0);
} else { // parent
Close(connfd);
}
}
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
void handle_sig(int signle)
{
printf("********\n");
}
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr;
char buf[MAXLINE];
int sockfd, n;
struct sigaction act;
act.sa_handler = handle_sig;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGPIPE,&act,NULL);
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while(fgets(buf, MAXLINE, stdin) != NULL) {
Write(sockfd, buf, strlen(buf));
n = Read(sockfd, buf, MAXLINE);
if ( n==0 ) {
printf("the other side has been closed.\n");
}else {
Write(STDOUT_FILENO, buf, n);
}
}
Close(sockfd);
return 0;
}
运行:
gcc server.c wrap.c -o server
gcc client.c wrap.c -o client
./server
./client