首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >目标C中的线程和套接字

目标C中的线程和套接字
EN

Stack Overflow用户
提问于 2010-12-05 07:12:45
回答 1查看 2.1K关注 0票数 2

注意:我编辑了我的问题。我用它来连接并执行第一个回调,但是后续的回调根本不通过。

这是我第一次写目标-C(与GNUstep;这是一个家庭作业)。我已经找到了解决方案,但我正试图在其中添加更多内容。该应用程序是一个GUI客户端,连接到服务器并从中获取数据。多个客户端可以连接到同一台服务器。如果任何一个客户端更改了驻留在服务器上的数据,服务器将向所有注册客户端发送回调。这个解决方案最初是用Java (客户机和服务器)实现的,对于最近的任务,教授希望我们为它编写一个目标C客户机。他说我们不需要处理回调,但我还是想试一试。

我使用的是NSThread,我写的东西如下所示:

CallbackInterceptorThread.h

代码语言:javascript
复制
#import <Foundation/Foundation.h>
#import "AppDelegate.h"

@interface CallbackInterceptorThread : NSThread {
   @private
   NSString* clientPort;
   AppDelegate* appDelegate;
}

- (id) initWithClientPort: (NSString*) aClientPort
              appDelegate: (AppDelegate*) anAppDelegate;
- (void) main;
@end

CallbackInterceptorThread.m

代码语言:javascript
复制
#import <Foundation/Foundation.h>
#import "CallbackInterceptorThread.h"

#define MAXDATASIZE 4096

@implementation CallbackInterceptorThread

- (id) initWithClientPort: (NSString*) aClientPort
                appDelegate: (AppDelegate*) anAppDelegate {

   if((self = [super init])) {
      [clientPort autorelease];
      clientPort = [aClientPort retain];
      [appDelegate autorelease];
      appDelegate = [anAppDelegate retain];
   }

   return self;
}

- (void) main {

   GSRegisterCurrentThread();

   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

   char* buffer = malloc(MAXDATASIZE);
   Cst420ServerSocket* socket = [[Cst420ServerSocket alloc] initWithPort: clientPort];
   [socket retain];

   NSString* returnString;

   while(YES) {
      printf("Client waiting for callbacks on port %s\n", [clientPort cString]);

      if([socket accept]) {
         printf("Connection accepted!\n");
         while(YES) {
            printf("Inner loop\n");
            sleep(1);

            returnString = [socket receiveBytes: buffer maxBytes: MAXDATASIZE beginAt: 0];
            printf("Received from Server |%s|\n", [returnString cString]);
            if([returnString length] > 0) {
               printf("Got a callback from server\n");

               [appDelegate populateGui];
            }

            printf("Going to sleep now\n");
            sleep(1);
         }

         [socket close];
      }
   }
}

@end

Cst420ServerSocket是由老师提供给我们的。看起来是这样的:

代码语言:javascript
复制
#import "Cst420Socket.h"
#define PORT "4444"

/**
 * Cst420Socket.m - objective-c class for manipulating stream sockets.
 * Purpose: demonstrate stream sockets in Objective-C.
 * These examples are buildable on MacOSX and GNUstep on top of Windows7
 */

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa){
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }
    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

@implementation Cst420ServerSocket

- (id) initWithPort: (NSString*) port{
   self = [super init];
   int ret = 0;
   memset(&hints, 0, sizeof hints);
   hints.ai_family = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags = AI_PASSIVE; // use my IP
   const char* portStr = [port UTF8String];
   if ((rv = getaddrinfo(NULL, portStr, &hints, &servinfo)) != 0) {
      fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
      ret = 1;
   }else{
      for(p = servinfo; p != NULL; p = p->ai_next) {
         if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1){
            perror("server: socket create error");
            continue;
         }
         if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
#if defined(WINGS)
         closesocket(sockfd);
#else
         close(sockfd);
#endif
            perror("server: bind error");
            continue;
         }
         break;
      }
      if (p == NULL)  {
         fprintf(stderr, "server: failed to bind\n");
         ret = 2;
      }else{
         freeaddrinfo(servinfo); // all done with this structure
         if (listen(sockfd, BACKLOG) == -1) {
            perror("server: listen error");
            ret = 3;
         }
      }
      if (ret == 0){
         return self;
      } else {
         return nil;
      }
   }
}

- (BOOL) accept {
   BOOL ret = YES;
#if defined(WINGS)
   new_fd = accept(sockfd, NULL, NULL);
#else
   new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
#endif
   if (new_fd == -1) {
      perror("server: accept error");
      ret = NO;
   }
   connected = ret;
   return ret;
}

- (int) sendBytes: (char*) byteMsg OfLength: (int) msgLength Index: (int) at{
   int ret = send(new_fd, byteMsg, msgLength, 0);
   if(ret == -1){
      NSLog(@"error sending bytes");
   }
   return ret;
}

- (NSString* ) receiveBytes: (char*) byteMsg
                   maxBytes: (int) max
                    beginAt: (int) at {
   int ret = recv(new_fd, byteMsg, max-1, at);
   if(ret == -1){
      NSLog(@"server error receiving bytes");
   }
   byteMsg[ret+at] = '\0';
   NSString * retStr = [NSString stringWithUTF8String: byteMsg];
   return retStr;
}

- (BOOL) close{
#if defined(WINGS)
   closesocket(new_fd);
#else
   close(new_fd);
#endif
   connected = NO;
   return YES;
}

- (void) dealloc {
#if defined(WINGS)
   closesocket(sockfd);
#else
   close(sockfd);
#endif
   [super dealloc];
}

@end

我们的教授还向我们提供了一个简单回显服务器和客户机的示例(服务器只会吐出客户机发送的任何内容),我在线程中使用了相同的模式。

我最初的问题是我的回调拦截器线程不接受来自服务器的任何(回调)连接。服务器说它无法连接回客户端(ConnectException来自Java,它说“连接拒绝”)。我可以通过修改指导员的代码来解决这个问题。在connect函数中(未显示),他将提示设置为使用AF_UNSPEC而不是AF_INET。因此,Java看到我的本地主机IP作为0:0:0:0:0:0:0:1 (以IPv6格式)出现。当Java试图连接回发送回调时,它会收到一个异常(不确定为什么不能连接到IPv6地址)。

在修复了这个问题之后,我再次试用了我的应用程序,这次来自服务器的回调被我的客户端接收到了。但是,随后的回调无法工作。在接收到第一个回调后,繁忙循环继续运行(正如它应该的那样)。但是,当服务器发送第二个回调时,客户端似乎无法读取它。在服务器端,我可以看到它成功地向客户端发送了回调。只是客户端在读取数据时遇到了困难。我添加了一些用于调试的打印语句(参见上文),这就是我所得到的:

代码语言:javascript
复制
Client waiting for callbacks on port 2020
Connection accepted!
Inner loop
Received from Server |A callback from server to 127.0.0.1:2020|
Got a callback from server
Going to sleep now
Inner loop
Received from Server ||
Going to sleep now
Inner loop
Received from Server ||
Going to sleep now
Inner loop
... (and it keeps going regardless of the second callback being sent)

下面是我如何启动线程(从GUI):

代码语言:javascript
复制
CallbackInterceptorThread* callbackInterceptorThread = [[CallbackInterceptorThread alloc] initWithClientPort: clientPort appDelegate: self];
[callbackInterceptorThread start];
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2010-12-05 09:03:48

我想我已经成功了。因此,在Java端(服务器),这就是我所做的:

代码语言:javascript
复制
Socket socket = new Socket(clientAddress, clientPort);
BufferedOutputStream out = new BufferedOutputStream(socket.getOutputStream());
out.write(("A callback from server to " + clientAddress + ":" + clientPort).getBytes());
out.flush();
out.close();

我在教授的代码中放置了一些调试打印语句,并注意到在receiveBytes中,recv返回0。recv的返回值是它收到的消息的长度。所以它收到了一个零长度的字符串。但是,返回值为0也意味着对等方正确地关闭了连接(这正是我在out.close()方面所做的)。因此,我想,如果我需要响应第二个回调,我需要再次accept连接。所以我把繁忙的循环改为:

代码语言:javascript
复制
printf("Client waiting for callbacks on port %s\n", [clientPort cString]);
while([socket accept]) {
   printf("Connection accepted!\n");    

   returnString = [socket receiveBytes: buffer maxBytes: MAXDATASIZE beginAt: 0];
   printf("Received from Server |%s|\n", [returnString cString]);

   if([returnString length] > 0) {
      printf("Got a callback from server\n");
      [appDelegate populateGui];
   }
}

[socket close];

这似乎起了作用。我不确定这是否是正确的做法,所以我愿意接受改进的建议!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4357783

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档