1. 程式人生 > >多程序和多執行緒簡單tcp聊天程式

多程序和多執行緒簡單tcp聊天程式

如果需要一個服務端可以連線多個客戶端,並同時與多個(不超多listen第二個引數及最大同時併發數)客戶端通訊,可以利用多程序即建立子程序,子程序來完成服務端的接受和傳送資料;也可以建立多個執行緒。對於tcp一些介面具體使用可以檢視這篇部落格:https://blog.csdn.net/sophia__yu/article/details/82827500 tcp客戶端程式碼

//tcp 客戶端程式碼
//1.建立套接字
//2.繫結地址資訊
//3.向服務端傳送連線請求
//4.傳送資料
//5.接受資料
//6.關閉socket描述符

#include<stdio.h>
#include<error.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<stdlib.h>

int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("Usage ip and port\n");
        }
        //1.建立套接字
        int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        if(sockfd<0)
        {
                perror("socket error");
                return -1;
        }
        //2.繫結地址資訊(不推薦手動寫繫結資訊程式碼)
        //3.向服務端傳送連線請求
        //int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
        struct sockaddr_in ser_addr;
        ser_addr.sin_family=AF_INET;
        ser_addr.sin_port=(htons)(atoi(argv[2]));
        ser_addr.sin_addr.s_addr=(inet_addr)(argv[1]);//因為argv[]是char*,用atoi使字串轉成整型
        int len=sizeof(struct sockaddr_in);
        int ret=connect(sockfd,(struct sockaddr*)&ser_addr,len);
        if(ret<0)
        {
                perror("connect error");
                close(sockfd);
                return -1;
        }
        //連線成功,socket描述符裡有服務端和客戶端IP地址和port
        while(1)
        {
                //4.傳送資料
                // ssize_t send(int sockfd, const void *buf, size_t len, int flags);
                char buff[1024]={0};
                printf("please send\n");
                scanf("%s",buff);
                ret=send(sockfd,buff,strlen(buff),0); //預設阻塞接受資料
                if(ret<0)
                {
                        perror("send error");
                        close(sockfd);
                        return -1;
                }
                //5.接受資料
                //ssize_t recv(int sockfd, void *buf, size_t len, int flags);
                memset(buff,0x00,1024);
                ret=recv(sockfd,buff,1023,0);//預設阻塞接受資料
                if(len<0)//小於0接受失敗
                {
                        perror("recv error");
                        close(sockfd);
                        continue;
                }
                else if(len==0)//等於0對端將連線斷開
                {
                   perror("peer has performed an orderly shutdown");
                   close(sockfd);
                   continue;
                }
                printf("[%s:%d]say:%s\n",(inet_ntoa)(ser_addr.sin_addr),(ntohs)(ser_addr.sin_port),buff);
        }
        close(sockfd);
        return 0;
}                                                

多程序服務端程式碼:

//多程序的tcp服務端程式碼

#include<stdio.h>
#include<unistd.h>
#include<error.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<signal.h>
#include<wait.h>
#include<stdlib.h>
#include<arpa/inet.h>

void profunc(int sockfd)
{
        int pid=fork();
        if(pid<0)
        {
                perror("fork error");
                close(sockfd);
                return ;
        }
        else if(pid==0){
                //子程序  接受資料和傳送資料
                while(1){
                        char buff[1024]={0};
                        int len=recv(sockfd,buff,1023,0);//阻塞接受資料
                        if(len<0)
                        {
                                perror("recv error");
                                close(sockfd);
                                return ;
                        }
                        else if(len==0)
                        {
                                perror("peer shutdown");
                                close(sockfd);
            return ;
                        }
                        printf("ser say:%s\n",buff);
                        send(sockfd,"hello",5,0);//服務端預設傳送hello

                }
                close(sockfd);
        }
        else
        {
                //父程序
                close(sockfd);//子程序和父程序的sockfd在不同空間,所以如果父程序不對sockfd做什麼,需要關閉,但是父程序最好不對sochfd不做什麼,否則無法區別傳送或接
受資料是父程序還是子程序
        }
}
void sigcb(int signum)
{
        while(waitpid(-1,NULL,WNOHANG)!=0)//沒有子程序退出將返回0,用迴圈是將等待子程序資源回收完畢
        {
        }
}
int main(int argc,char* argv[])
{
        signal(SIGCHLD,sigcb);//將SIGCHLD自定義設定為等待子程序退出功能
        if(argc!=3)
        {
                printf("Usage:./ ip  port\n");
        }
        //1.建立套接字
        int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//6
        if(sockfd<0)
        {
                perror("socket error");
                return -1;
        }
        //2.繫結地址資訊
 struct sockaddr_in ser_addr;
        ser_addr.sin_family=AF_INET;
        ser_addr.sin_port=htons((atoi)(argv[2]));
        ser_addr.sin_addr.s_addr=(inet_addr)(argv[1]);
        int len=sizeof(struct sockaddr_in);
        int ret=bind(sockfd,(struct sockaddr*)&ser_addr,len);
        if(ret<0)
        {
                perror("bind error");
                close(sockfd);
                return -1;
        }
        //3.監聽,listen只是設定好socket的屬性,連線成功佇列最多有5個結點
        if(listen(sockfd,5)<0)
        {
                perror("losten error");
                close(sockfd);
                return -1;
        }
        //4.獲取新連線的客戶端資訊
        //多程序,建立子程序,子程序來接受傳送資料,從而實現一個服務端可以同時連線多個客戶端
        while(1)
        {
                struct sockaddr_in cli_addr;
                len=sizeof(struct sockaddr_in);
                int newsockfd=accept(sockfd,(struct sockaddr*)&cli_addr,&len);
                profunc(newsockfd);
        }
        close(sockfd);
        return 0;
}

第一個客戶端: 在這裡插入圖片描述 第二個客戶端: 在這裡插入圖片描述 服務端: 在這裡插入圖片描述 多執行緒tcp服務端程式碼

//多程序的tcp服務端程式碼

#include<stdio.h>
#include<unistd.h>
#include<error.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<signal.h>
#include<wait.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<pthread.h>

void* the_start(void* arg)
{
        int sockfd=(int)arg;
        while(1)
        {
                //接受資料
                char buff[1024]={0};
                int len=recv(sockfd,buff,1023,0);//阻塞接受資料
                if(len<0)
                {
                        perror("recv error");
                        close(sockfd);
                        return ;
                }
                else if(len==0)
                {
                        perror("peer shutdown");
                        close(sockfd);
                        return ;
                }
                printf("ser say:%s\n",buff);
                send(sockfd,"hello",5,0);//服務端預設傳送hello
        }
        close(sockfd);
}
int main(int argc,char* argv[])
{
        if(argc!=3)
 {
                printf("Usage:./ ip  port\n");
        }
        //1.建立套接字
        int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//6
        if(sockfd<0)
        {
                perror("socket error");
                return -1;
        }
        //2.繫結地址資訊
        struct sockaddr_in ser_addr;
        ser_addr.sin_family=AF_INET;
        ser_addr.sin_port=htons((atoi)(argv[2]));
        ser_addr.sin_addr.s_addr=(inet_addr)(argv[1]);
        int len=sizeof(struct sockaddr_in);
        int ret=bind(sockfd,(struct sockaddr*)&ser_addr,len);
        if(ret<0)
        {
         perror("bind error");
                close(sockfd);
                return -1;
        }
        //3.監聽,listen只是設定好socket的屬性,連線成功佇列最多有5個結點
        if(listen(sockfd,5)<0)
        {
                perror("losten error");
                close(sockfd);
                return -1;
        }
        //4.獲取新連線的客戶端資訊
        //建立多個執行緒
        while(1)
        {
                struct sockaddr_in cli_addr;
                len=sizeof(struct sockaddr_in);
                int newsockfd=accept(sockfd,(struct sockaddr*)&cli_addr,&len);
                //建立執行緒
// // int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
                pthread_t tid;
                int ret;
                ret=pthread_create(&tid,NULL,the_start,(void*)newsockfd);
        }
        close(sockfd);
        return 0;
}

第一個客戶端: 在這裡插入圖片描述 第二個客戶端: 在這裡插入圖片描述 服務端: 在這裡插入圖片描述