(7条消息) Linux下TCP/IP编程

本文参考自徐晓鑫《后台开发》,重点复习总结TCP通信流程,读者也可以参考:
http://blog.csdn.net/wqc_csdn/article/details/51513543,谢谢。

一、客户端和服务端操作流程

服务器端
socket() --> bind() --> listen() --> accept() --> recv() --> close()
创建socket --> 绑定socket和端口号–> 监听端口号–> 接收来自客户端的连接请求–> 从socket中读取字符–> 关闭socket

客户端
socket() --> connect() --> send() --> close()
创建socket --> 连接指定服务器的IP/端口号–>向socket中写入信息–>关闭socket

二、代码

服务器端

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <unistd.h>#define MAXLINE 4096int main(){    int listenfd,connfd;    struct sockaddr_in servaddr;    char buff[4096];    int n;    //创建一个TCP的socket    if( (listenfd = socket(AF_INET,SOCK_STREAM,0)) == -1) {        printf(" create socket error: %s (errno :%d)\n",strerror(errno),errno);        return 0;    }    //先把地址清空,检测任意IP    memset(&servaddr,0,sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    servaddr.sin_port = htons(6666);    //地址绑定到listenfd    if ( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {        printf(" bind socket error: %s (errno :%d)\n",strerror(errno),errno);        return 0;    }    //监听listenfd    if( listen(listenfd,10) == -1) {        printf(" listen socket error: %s (errno :%d)\n",strerror(errno),errno);        return 0;    }    printf("====waiting for client's request=======\n");    //accept 和recv,注意接收字符串添加结束符'\0'    while(1)    {        if( (connfd = accept(listenfd, (struct sockaddr *)NULL, NULL))  == -1) {            printf(" accpt socket error: %s (errno :%d)\n",strerror(errno),errno);            return 0;        }        n = recv(connfd,buff,MAXLINE,0);        buff[n] = '\0';        printf("recv msg from client:%s\n",buff);        close(connfd);    }    close(listenfd);    return 0;}

客户端

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#define MAXLINE 4096int main(int argc, char**argv){    int sockfd,n;    char recvline[4096],sendline[4096];    struct sockaddr_in servaddr;    if(argc !=2)    {        printf("usage: ./client <ipaddress>\n");        return 0;    }    //创建socket    if( (sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) {        printf(" create socket error: %s (errno :%d)\n",strerror(errno),errno);        return 0;    }        memset(&servaddr,0,sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(6666);    //IP地址从“点分十进制”转换到“二进制整肃”    if( inet_pton(AF_INET,argv[1], &servaddr.sin_addr) <=0 ) {        printf("inet_pton error for %s\n",argv[1]);        return 0;    }    //连接    if( connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0) {        printf(" connect socket error: %s(errno :%d)\n",strerror(errno),errno);        return 0;    }    printf("send msg to server:\n");    fgets(sendline,4096,stdin);    //send发送    if ( send(sockfd,sendline,strlen(sendline),0) <0) {        printf("send msg error: %s(errno :%d)\n",strerror(errno),errno);        return 0;    }    close(sockfd);    return 0;}

运行
g++ server.cpp -o server
g++ client.cpp -o client
分别打开两个终端窗口
一个执行./server命令,

一个执行./client 1227.0.0.1命令。

三、错误记录

在调试过程中发现错误:

socket error: Socket operation on non-socket(errno :88)

经过仔细分析发现:
if( (sockfd = socket(AF_INET,SOCK_STREAM,0))== -1)
是优先级出现了问题,这里必须先复制,之后进行关系运算符的比较。
本文风格的代码非常容易在这里犯错误,特此记录。

四、多线程并发服务器

2018年11月3日补充

多线程和多进程的处理方式类似,都是创建一个新的线程,客户端有请求时,用新创建的线程处理。
这里参考文章:多线程服务器,对代码进行模块化封装。
和上文相比,将客户端修改为循环发送,直至输入q退出
犯的一个错误是服务器printf打印忘记加\n,导致没有刷新缓冲区,只有客户端每次端开链接时才打印,汗~

//使用pthread线程库#include<stdio.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<sys/socket.h>#include<sys/types.h>#include<sys/stat.h>#include<netinet/in.h>#include<arpa/inet.h>#include<pthread.h>#define PORT 6666            //服务器端口#define BACKLOG 5            //listen队列等待的连接数#define MAXDATASIZE  1024    //缓冲区大小 void process_cli(int connectfd, struct sockaddr_in client);    //客户端请求处理函数void* start_routine(void* arg);        //线程函数 typedef struct  _ARG  {    int connfd;    struct sockaddr_in client; }ARG;                      //客户端结构体void sys_err(const char * ptr_err){    perror(ptr_err);    exit(EXIT_FAILURE);}//处理客户端链接的接收工作*void accept_conn(int listenfd){ int  connectfd;          //socket描述符     pthread_t  connectthread;   //线程体变量     ARG *arg;                      //客户端结构体变量 struct sockaddr_in client;     //客户端地址信息结构体     int sin_size = sizeof(struct sockaddr_in);  while(1) {        //调用accept,返回与服务器连接的客户端描述符        if ((connectfd = accept(listenfd,(struct sockaddr *)&client,(socklen_t *)&sin_size))==-1) {                                sys_err("accept() error\n");        }                arg = new  ARG;        arg->connfd = connectfd;        memcpy(&arg->client, &client, sizeof(client));        //创建线程,以客户端连接为参数,start_routine为线程执行函数        if (pthread_create(&connectthread, NULL, start_routine, (void*)arg)) {                    sys_err("Pthread_create() error");        }    }} void tcp_server(uint16_t port){int listenfd;   //socket描述符    struct sockaddr_in server;     //服务器地址信息结构体       //调用socket,创建监听客户端的socket    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {         sys_err("Creating socket failed.");    }    //设置socket属性,端口可以重用    int opt = SO_REUSEADDR;            setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));        //初始化服务器地址结构体    bzero(&server,sizeof(server));    server.sin_family=AF_INET;    server.sin_port=htons(PORT);    server.sin_addr.s_addr = htonl (INADDR_ANY);    //调用bind,绑定地址和端口    if (bind(listenfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) {           sys_err("Bind error.");    }       //调用listen,开始监听    if(listen(listenfd,BACKLOG) == -1){              sys_err("listen() error\n");    }    //处理客户端链接的接收工作    accept_conn(listenfd);//关闭监听socket    close(listenfd);        }int main(){    uint16_t port  = 6666;tcp_server(port);    } void process_cli(int connectfd, sockaddr_in client){    int num;    char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];        printf("You got a connection from %s.  \n",inet_ntoa(client.sin_addr) );//MSG_WAITALL     while ((num = recv(connectfd, recvbuf, MAXDATASIZE,0)) > 0) { recvbuf[num] = '\0';        printf("Received size( %d ) message: %s\n",num, recvbuf);}       /*  num = recv(connectfd, cli_name, MAXDATASIZE,0);    if (num == 0) {        close(connectfd);                return;    }    cli_name[num - 1] = '\0';    printf("Client's content is: %s\n",cli_name);    while (num = recv(connectfd, recvbuf, MAXDATASIZE,0)) {        recvbuf[num] = '\0';        printf("Received client( %s ) message: %s",cli_name, recvbuf);        for (int i = 0; i < num - 1; i++) {            sendbuf[i] = recvbuf[num - i -2];        }        sendbuf[num - 1] = '\0';        send(connectfd,sendbuf,strlen(sendbuf),0);    }*/printf("Client disconnected.\n");    close(connectfd); }void* start_routine(void* arg){         ARG *info = (ARG *)arg;    process_cli(info->connfd, info->client);//删除对应的堆内存delete info;    pthread_exit(NULL);} 

客户端:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#define MAXLINE 4096int main(int argc, char**argv){    int sockfd,n;    char recvline[4096],sendline[4096];    struct sockaddr_in servaddr;    if(argc !=2)    {        printf("usage: ./client <ipaddress>\n");        return 0;    }    //创建socket    if( (sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) {        printf(" create socket error: %s (errno :%d)\n",strerror(errno),errno);        return 0;    }    memset(&servaddr,0,sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(6666);    //IP地址从“点分十进制”转换到“二进制整肃”    if( inet_pton(AF_INET,argv[1], &servaddr.sin_addr) <=0 ) {        printf("inet_pton error for %s\n",argv[1]);        return 0;    }    //连接    if( connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0) {        printf(" connect socket error: %s(errno :%d)\n",strerror(errno),errno);        return 0;    }    printf("send msg to server:\n");    while(1){fgets(sendline,sizeof(sendline),stdin);sendline[strlen(sendline)-1] = '\0';if(strncasecmp(sendline,"q",1) == 0) {printf("quit\n");break;}//send发送//if ( send(sockfd,sendline,strlen(sendline),0) < 0) {//    printf("send msg error: %s(errno :%d)\n",strerror(errno),errno);//    return 0;//}int sentbytes = send(sockfd,sendline,strlen(sendline),0); printf("sent:%d\n",sentbytes);//bzero(sendline, strlen(sendline));}    close(sockfd);    return 0;}
(0)

相关推荐