TCP实现局域网通信

TCP客户端通信步骤:
1:创建套接字 sockfd = socket(AF_INET ,SOCK_STREAM ,0); 2:填写服务器结构体信息

struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));

其中服务器信息结构体要用sockaddr_in创建
3:发送客户端连接请求

connect(sockfd, (struct sockaddr *)&serveraddr,addrlen);

注意服务器信息结构体要进行强制类型转换
4:进行通信

recv(sockfd, buf1, 128, 0);
send(sockfd, buf, 128, 0);
注意send和recv可以设置阻塞和非阻塞 recv默认阻塞

其中可以通过创建进程和线程实现接收和发送
5:关闭套接字
TCP服务器通信步骤:
1:创建套接字

sockfd = socket(AF_INET, SOCK_STREAM, 0);

2:将套接字和服务器信息结构体绑定

struct sockaddr_in serveraddr, clientaddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
bind(sockfd, (struct sockaddr *)&serveraddr, addrlen);

3:将套接字设置为监听状态

listen(sockfd, 5)

4:阻塞等待客户端连接请求

acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)

5:进行通信
6:关闭套接字
关闭监听套接字导致服务器无法建立新连接,但不影响已建立连接
关闭accept返回的套接字代表该套接字的连接关闭,不影响服务器监听
实现TCP并发服务器
客户端代码:client_Ancy.c

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //htons inet_addr

z
	char buf1[128] = "";
        pthread_t thread;
	int sockfd;
	char ip[] = "180.201.00.00";
	char port[] = "8080";
void thread_do(){
while(1){

	fgets(buf,128,stdin);
	buf[strlen(buf) - 1] = '\0';
	
	send(sockfd, buf, 128, 0);

}
}
int main(int argc, char const *argv[])
{
	if (argc<3){
        printf("Usage: %s [ip] [port]\n", argv[0]);
        exit(1);
	}

	sockfd = socket(AF_INET ,SOCK_STREAM ,0);
	
	
         struct sockaddr_in serveraddr;
        socklen_t addrlen = sizeof(serveraddr);
        
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
        serveraddr.sin_port = htons(atoi(argv[2]));
        //serveraddr.sin_addr.s_addr = inet_addr(ip);
        //serveraddr.sin_port = htons(atoi(port));

	
	connect(sockfd, (struct sockaddr *)&serveraddr,addrlen);
	
	pthread_create(&thread, NULL, (void *)&thread_do,NULL);//此时可以通过把thread_do设置为指针函数,即定义时void * thread_do()参数可以写thread_do
	
while(1){
	memset(buf1,0,128);
	recv(sockfd, buf1, 128, 0);
	printf("recv:%s\n",buf1);
	}
	close(sockfd);

}

多进程实现服务器代码:server_Ancy.c

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

//使用多进程实现TCP并发服务器

#define N 128
#define ERR_LOG(errmsg) do{\
                            perror(errmsg);\
                            exit(1);\
                        }while(0)

void handler(int sig)
{
    wait(NULL);
}

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <server_ip> <server_port>\n", argv[0]);
        exit(1);
    }    

    int sockfd, acceptfd;
    struct sockaddr_in serveraddr, clientaddr;
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        ERR_LOG("fail to socket");
    }

    //将套接字设置为允许重复使用本机地址或者为设置为端口复用
    int on = 1;
    if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    {
        ERR_LOG("fail to setsockopt");
    }

    //第二步:填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:将套接字与服务器网络信息结构体绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
    {
        ERR_LOG("fail to bind");
    }

    //第四步:将套接字设置为被动监听状态
    if(listen(sockfd, 5) < 0)
    {
        ERR_LOG("fail to listen");
    }

    //使用型号,异步的方式处理僵尸进程
    signal(SIGCHLD, handler);

    while(1)
    {
        //第五步:阻塞等待客户端的连接请求
        if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0)
        {
            ERR_LOG("fail to accept");
        }

        //打印客户端的信息
        printf("%s -- %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

        //使用fork函数创建子进程,父进程继续负责连接,子进程负责与客户端通信
        pid_t pid;
        if((pid = fork()) < 0)
        {
            ERR_LOG("fail to fork");
        }
        else if(pid > 0) //父进程负责执行accept,所以if语句结束后继续在accept函数的位置阻塞
        {
        }
        else //子进程负责跟指定的客户端通信
        {
            char buf[N] = "";
            ssize_t bytes;
            while (1)
            {
                if((bytes = recv(acceptfd, buf, N, 0)) < 0)
                {
                    ERR_LOG("fail to recv");
                }
                else if(bytes == 0)
                {
                    printf("The client quited\n");
                    exit(0);
                }

                if(strncmp(buf, "quit", 4) == 0)
                {
                    exit(0);
                }

                printf("from client: %s\n", buf);

                strcat(buf, " ^_^");
                if(send(acceptfd, buf, N, 0) < 0)
                {
                    ERR_LOG("fail to send");
                }
            }
        }
    }

    return 0;
}

多线程实现服务器代码:server_Ancy.c

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

#define N 128
#define ERR_LOG(errmsg) do{\
                            perror(errmsg);\
                            exit(1);\
                        }while(0)

typedef struct{
    struct sockaddr_in addr;
    int acceptfd;
}MSG;

void *pthread_fun(void *arg)
{
    char buf[N] = "";
    ssize_t bytes;
    MSG msg = *(MSG *)arg;
    while (1)
    {
        if((bytes = recv(msg.acceptfd, buf, N, 0)) < 0)
        {
            ERR_LOG("fail to recv");
        }
        else if(bytes == 0)
        {
            printf("The client quited\n");
            pthread_exit(NULL);
        }

        if(strncmp(buf, "quit", 4) == 0)
        {
            printf("The client quited\n");
            pthread_exit(NULL);
        }

        printf("[%s - %d]: %s\n", inet_ntoa(msg.addr.sin_addr), ntohs(msg.addr.sin_port), buf);

        strcat(buf, " ^_^");
        if(send(msg.acceptfd, buf, N, 0) < 0)
        {
            ERR_LOG("fail to send");
        }
    }
}

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <server_ip> <server_port>\n", argv[0]);
        exit(1);
    }    

    int sockfd, acceptfd;
    struct sockaddr_in serveraddr, clientaddr;
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        ERR_LOG("fail to socket");
    }

    //将套接字设置为允许重复使用本机地址或者为设置为端口复用
    int on = 1;
    if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    {
        ERR_LOG("fail to setsockopt");
    }

    //第二步:填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:将套接字与服务器网络信息结构体绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
    {
        ERR_LOG("fail to bind");
    }

    //第四步:将套接字设置为被动监听状态
    if(listen(sockfd, 5) < 0)
    {
        ERR_LOG("fail to listen");
    }

    while(1)
    {
        //第五步:阻塞等待客户端的连接请求
        if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0)
        {
            ERR_LOG("fail to accept");
        }

        //打印客户端的信息
        //printf("%s -- %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

        //创建子线程与客户端进行通信
        MSG msg;
        msg.addr = clientaddr;
        msg.acceptfd = acceptfd;
        pthread_t thread;
        if(pthread_create(&thread, NULL, pthread_fun, &msg) != 0)
        {
            ERR_LOG("fail to pthread_create");
        }
        pthread_detach(thread);
    }

    return 0;
}