linux TCP socket 实例

来源:互联网 发布:python 视频 编辑:程序博客网 时间:2024/05/22 15:50

0:TCP/IP协议栈与数据包封装

CP/IP网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路层(Link)四层。


不同的协议层对数据包有不同的称谓,在传输层叫做段(segment)在网络层叫做数据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理。。IP地址是标识网络中不同主机的地址,而端口号就是同一台主机上标识不同进程的地址,IP地址和端口号合起来标识网络中唯一的进程。

1:基于TCP服务端和客户端系统调用


由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。注意,客户端不是不允许调用bind(),只是没有必要调用bind()固定一个端口号,服务器也不是必须调用bind(),但如果服务器不调用bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器就会遇到麻烦。

2:socket 地址说明

Generic Socket Address Structures通用socket address
struct sockaddr {sa_family_t sa_family; /* Address family (AF_* constant) */char sa_data[14]; /* Socket address (size variesaccording to socket domain) */};struct sockaddr_in { /* IPv4 socket address */sa_family_t sin_family; /* Address family (AF_INET) */in_port_t sin_port; /* Port number */struct in_addr sin_addr; /* IPv4 address */unsigned char __pad[X]; /* Pad to size of 'sockaddr'structure (16 bytes) */};struct in_addr { /* IPv4 4-byte address */in_addr_t s_addr; /* Unsigned 32-bit integer */};

在linux socket编程中使用的socket地址都为通用格式即:struct sockaddr
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);Returns 0 on success, or –1 on error
对于IPv4,family参数指定为AF_INET。
对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。
对于UDP协议,type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。
protocol参数的介绍从略,指定为0即可。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); Returns 0 on success, or –1 on error
int listen(int sockfd, int backlog); Returns 0 on success, or –1 on error 
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); Returns file descriptor on success, or –1 on error
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);Returns 0 on success, or –1 on error 
比如在bind(),accept(),connect()中的函数中都使用的是struct sockaddr地址,但是在实际的使用中使用的是:struct sockaddr_in地址。因此代码中常用的地址格式定义如下:
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_addr.s_addr = inet_addr("192.168.100.244");
servaddr.sin_port = htons(SERV_PORT);
struct sockaddr_in中的成员struct in_addr sin_addr表示32位的IP地址。实际中多数使用点分十进制的字符串表示IP地址,以下函数可以实现字符串表示和in_addr表示之间转换。
字符串转in_addr的函数:
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr);
int inet_pton(int family, const char *strptr, void *addrptr);
in_addr转字符串的函数:
char *inet_ntoa(struct in_addr inaddr);
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

3:TCP服务端和客户端代码框架。

服务端:通过server ip和port 返回socketID
int getServerSokcketId(int argc, char *argv[]){struct sockaddr_in servaddr;int listenfd,optval = 1;;if (argc != 3){fputs("usage: ./server serverIp serverPort\n", stderr);exit(1);}listenfd = socket(AF_INET, SOCK_STREAM, 0);if(listenfd == -1){    close(listenfd);perror("create server socket ....");exit(0);}bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;//servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_addr.s_addr = inet_addr(argv[1]);servaddr.sin_port = htons(atoi(argv[2]));  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval,sizeof(optval)) == -1)   {        close(listenfd);perror("server socket  setsockopt....");        return -1;  }if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1){perror("server bind ....");    close(listenfd);exit(0);}if(listen(listenfd, 20) == -1){perror("server listen ....");    close(listenfd);exit(0);}printf("Accepting connections ...\n");return listenfd;}
main函数调用,同时完成对客户端的请求处理。
int main(int argc, char *argv[]){struct sockaddr_in  cliaddr;socklen_t cliaddr_len;int listenfd, acceptFd;    listenfd = getServerSokcketId(argc,argv);while(1){cliaddr_len = sizeof(cliaddr);acceptFd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);  /* Wait for connection */if (acceptFd == -1){syslog(LOG_ERR, "Failure in accept(): %s",strerror(errno));continue;           /* Try next */}else{printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));}#if 0switch (fork()) //使用多进程处理用户的请求{       /* Create child for each client */case -1:printf("------------------------------fork() return error\n");syslog(LOG_ERR, "111Can't create child (%s)",strerror(errno));close(acceptFd);         /* Give up on this client */break;              /* May be temporary; try next client */case 0:                 /* Child */close(listenfd);         /* Don't need copy of listening socket */handleRequest(acceptFd);printf("----handleRequest execute over--------------------\n");_exit(EXIT_SUCCESS);default:                /* Parent */close(acceptFd);         /* Don't need copy of connected socket */break;              /* Loop to accept next connection */}#endifclientProcess(acceptFd);//使用多线程处理客户端的请求。 }}
其中函数clientProcess(acceptFd);定义如下:
void *thr_fn1(int cfd){ssize_t numRead,numSocketRead,numSocketWrite;char message[1000];while(1){if((numSocketRead = read(cfd, &message, sizeof(message))) > 0){//此处对收到的message进行处理numSocketWrite=write(cfd, &message, sizeof(message));if ((numSocketWrite != sizeof(message)) || numSocketWrite == -1) {syslog(LOG_ERR, "write() failed: %s", strerror(errno));return(EXIT_FAILURE);}}f(numSocketRead <= 0){printf("process pid:%d exit\n",getpid());close(cfd);syslog(LOG_ERR, "Error from read(): %s", strerror(errno));return(EXIT_FAILURE);}}}void clientProcess(int cfd){  pthread_t tid1;pthread_create(&tid1, NULL, thr_fn1, cfd);}
客户端代码
以下函数返回客户端socketID跟服务端类似。
int getClietnSocketId(int argc, char *argv[]){struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd, n;if (argc != 3){fputs("usage: ./client serverIp serverPort\n", stderr);exit(1);}sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd == -1){perror("create socket....\n");}bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1], &servaddr.sin_addr.s_addr);if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr.s_addr) <= 0){printf("set ip address error!\n");exit(0);}//inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr.s_addr);if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))==-1){perror("connect....");exit(0);}return sockfd;}
main函数
int main(int argc, char *argv[]){int sfd=0,numRead;char  parentMsg[1000];    char  message[1000];    char buf[BUF_SIZE];    memset(&message,0,sizeof(message));    memset(&parentMsg,0,sizeof(parentMsg));sfd= getClietnSocketId(argc,argv);    if (sfd == -1)  {  errExit("inetConnect");  }    switch (fork())     {    case -1:        errExit("fork");    case 0:             /* Child: read server's response, echo on stdout */    memset(&message,0,sizeof(message));        for (;;) {        //memset(&message,0,sizeof(struct fileMessage));   mutex = pthread_mutex_lock(&mtx);   if (mutex != 0)      printf("pthread_mutex_lock");            if((numRead = read(sfd, &message, sizeof(message)))>0)            {            }   mutex = pthread_mutex_unlock(&mtx);   if (mutex!= 0)      printf("pthread_mutex_unlock");            if (numRead <= 0)                   /* Exit on EOF or error */            {            printf("server has closed!..%s\n",strerror(errno));            break;            }        }        printf("-------------fork exit, message: %s----------------------------------\n",strerror(errno));        _exit(EXIT_SUCCESS);    default:            /* Parent: write contents of stdin to socket */    while(1)    {     if((numRead = read(STDIN_FILENO, buf, BUF_SIZE))>0)    {           if((write(sfd, &parentMsg, sizeof(parentMsg))) != sizeof(parentMsg))           {           fatal("write() failed");           close(sfd)           }        }      }            }        exit(EXIT_SUCCESS);    }}

4:执行结果