一个hello/hi的简单的网络聊天程序

一、Socket API函数

Linux 下使用 <sys/socket.h> 头文件中 socket() 函数来创建套接字,原型为:
  int socket(int af, int type, int protocol);
其中:af 为地址族(Address Family),即:IP 地址类型,常用的有 AF_INET和PF_INET。
type为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向链接套接字)和SOCK_DGRAM(数据报套接字/无连接的套接字)。
protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。

bind() 函数的原型为:
  int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
其中:sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。

connect() 函数用来建立连接,它的原型为:
  int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);
其参数和 bind() 相同。

对于服务器端程序,使用 bind() 绑定套接字后,还需要使用 listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求了。
通过 listen() 函数可以让套接字进入被动监听状态,它的原型为:
  int listen(int sock, int backlog);
其中:sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度。
所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。

当套接字处于监听状态时,可以通过 accept() 函数来接收客户端请求。它的原型为:
  int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
其中:它的参数与 listen() 和 connect() 是相同的:sock 为服务器端套接字,addr 为 sockaddr_in 结构体变量,addrlen 为参数 addr 的长度,可由 sizeof() 求得。

Linux 不区分套接字文件和普通文件,使用 write() 可以向套接字中写入数据,使用 read() 可以从套接字中读取数据。
write() 的原型为:
  ssize_t write(int fd, const void *buf, size_t nbytes);
其中:fd 为要写入的文件的描述符,buf 为要写入的数据的缓冲区地址,nbytes 为要写入的数据的字节数。
read() 的原型为:
  ssize_t read(int fd, void *buf, size_t nbytes);
其中:fd 为要读取的文件的描述符,buf 为要接收数据的缓冲区地址,nbytes 为要读取的数据的字节数。


二、简单的网络聊天程序

下面是简单的网络聊天程序的代码。

server.c 是服务器端代码,

#include <stdio.h>          /* perror */
#include <stdlib.h>         /* exit */
#include <sys/types.h>      /* WNOHANG */
#include <sys/wait.h>       /* waitpid */
#include <string.h>         /* memset */
#include "socketwrapper.h"  /* socket layer wrapper */
#define true        1
#define false       0

#define MYPORT      12334                /* 监听的端口 */
#define BACKLOG     10                  /* listen的请求接收队列长度 */
#define MAXSIZE     100
int main() 
{
    int numbytes=0;
    int sockfd, new_fd;            /* 监听端口,数据端口 */
    struct sockaddr_in sa;         /* 自身的地址信息 */ 
    struct sockaddr_in client_addr; /* 连接对方的地址信息 */
    int sin_size;
    char buf[MAXSIZE];
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 
        {
            perror("socket"); 
            exit(1); 
        }
    sa.sin_family = AF_INET;
    sa.sin_port = Htons(MYPORT);         /* 网络字节顺序 */
    sa.sin_addr.s_addr = INADDR_ANY;     /* 自动填本机IP */
    memset(&(sa.sin_zero),0, 8);            /* 其余部分置0 */
    if ( Bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1) 
        {
            perror("bind");
            exit(1);
        }
    if (Listen(sockfd, BACKLOG) == -1) 
        {
            perror("listen");
            exit(1);
        }
    sin_size = sizeof(struct sockaddr_in);
    new_fd = Accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
    if (new_fd == -1)
        {

            perror("accept");

            exit(1);

        }

    printf("Got connection from %s\n", Inet_ntoa(client_addr.sin_addr));

    /* 主循环 */

    while(1) 

        {

                if ((numbytes=recv(new_fd,buf,MAXSIZE,0))>0)

                {

                    buf[numbytes]=0;

                    printf("received:%s   &   length=%d\n",buf,numbytes);

                    if(send(new_fd,buf,strlen(buf), 0) == -1)

                    perror("send");

                }

        }

    return true;

}

client.c 是客户端代码,

#include <stdio.h>          /* perror */

#include <stdlib.h>         /* exit */

#include <sys/types.h>      /* WNOHANG */

#include <sys/wait.h>       /* waitpid */

#include <string.h>         /* memset */

#include "socketwrapper.h"  /* socket layer wrapper */



#define true        1

#define false       0



#define PORT        12334   /* Server的端口 */

#define MAXDATASIZE 100     /*一次可以读的最大字节数 */



int main(int argc, char *argv[])

{

    int sockfd, numbytes;

    char buf[MAXDATASIZE];

    struct hostent *he; /* 主机信息 */

    struct sockaddr_in server_addr; /* 对方地址信息 */

    if (argc != 2) 

        {

            fprintf(stderr,"usage: client hostname\n");

            exit(1);

        }

    /* get the host info */

    if ((he=Gethostbyname(argv[1])) == NULL) 

        {

            /* 注意:获取DNS信息时,显示出错需要用herror而不是perror */

            /* herror 在新的版本中会出现警告,已经建议不要使用了 */

            perror("gethostbyname");

            exit(1);

        }

    if ((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1) 

        {

            perror("socket");

            exit(1);

        }

    

    server_addr.sin_family = AF_INET;

    server_addr.sin_port = Htons(PORT); /* short, NBO */

    server_addr.sin_addr = *((struct in_addr *)he->h_addr_list[0]);

    memset(&(server_addr.sin_zero),0, 8); /* 其余部分设成0 */



    if (Connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)    

        {

            perror("connect");

            exit(1);

        }



    while(1)

    {
      printf("Enter Something:");
      scanf("%s",buf);
      numbytes=send(sockfd,buf,strlen(buf),0);
      numbytes=recv(sockfd,buf,MAXDATASIZE,0);
      buf[numbytes]='\0';
     printf("received:%s\n",buf);

    }

    Close(sockfd);
    return true;

}

 运行结果:

  

  

 客户端和服务器的通信过程如下图所示:

   

原文地址:https://www.cnblogs.com/LiScott/p/12019120.html